mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: checklist improve (#2653)
* feat: improve checklist * feat: reimplement checklist * test: fix
This commit is contained in:
@ -592,7 +592,7 @@ impl FieldType {
|
||||
self == &MULTI_SELECT_FIELD || self == &SINGLE_SELECT_FIELD
|
||||
}
|
||||
|
||||
pub fn is_check_list(&self) -> bool {
|
||||
pub fn is_checklist(&self) -> bool {
|
||||
self == &CHECKLIST_FIELD
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,98 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::SelectOptionPB;
|
||||
use crate::services::field::checklist_type_option::ChecklistCellData;
|
||||
|
||||
use crate::services::field::SelectOption;
|
||||
use collab_database::rows::RowId;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct ChecklistCellDataPB {
|
||||
#[pb(index = 1)]
|
||||
pub options: Vec<SelectOptionPB>,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub selected_options: Vec<SelectOptionPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub(crate) percentage: f64,
|
||||
}
|
||||
|
||||
impl From<ChecklistCellData> for ChecklistCellDataPB {
|
||||
fn from(cell_data: ChecklistCellData) -> Self {
|
||||
let selected_options = cell_data.selected_options();
|
||||
let percentage = cell_data.percentage_complete();
|
||||
Self {
|
||||
options: cell_data
|
||||
.options
|
||||
.into_iter()
|
||||
.map(|option| option.into())
|
||||
.collect(),
|
||||
selected_options: selected_options
|
||||
.into_iter()
|
||||
.map(|option| option.into())
|
||||
.collect(),
|
||||
percentage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct ChecklistCellDataChangesetPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub row_id: String,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub insert_options: Vec<String>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub selected_option_ids: Vec<String>,
|
||||
|
||||
#[pb(index = 6)]
|
||||
pub delete_option_ids: Vec<String>,
|
||||
|
||||
#[pb(index = 7)]
|
||||
pub update_options: Vec<SelectOptionPB>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChecklistCellDataChangesetParams {
|
||||
pub view_id: String,
|
||||
pub field_id: String,
|
||||
pub row_id: RowId,
|
||||
pub insert_options: Vec<String>,
|
||||
pub selected_option_ids: Vec<String>,
|
||||
pub delete_option_ids: Vec<String>,
|
||||
pub update_options: Vec<SelectOption>,
|
||||
}
|
||||
|
||||
impl TryInto<ChecklistCellDataChangesetParams> for ChecklistCellDataChangesetPB {
|
||||
type Error = FlowyError;
|
||||
|
||||
fn try_into(self) -> Result<ChecklistCellDataChangesetParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
|
||||
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
||||
let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
|
||||
Ok(ChecklistCellDataChangesetParams {
|
||||
view_id: view_id.0,
|
||||
field_id: field_id.0,
|
||||
row_id: RowId::from(row_id.0),
|
||||
insert_options: self.insert_options,
|
||||
selected_option_ids: self.selected_option_ids,
|
||||
delete_option_ids: self.delete_option_ids,
|
||||
update_options: self
|
||||
.update_options
|
||||
.into_iter()
|
||||
.map(SelectOption::from)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
mod checkbox_entities;
|
||||
mod checklist_entities;
|
||||
mod date_entities;
|
||||
mod number_entities;
|
||||
mod select_option;
|
||||
@ -6,6 +7,7 @@ mod text_entities;
|
||||
mod url_entities;
|
||||
|
||||
pub use checkbox_entities::*;
|
||||
pub use checklist_entities::*;
|
||||
pub use date_entities::*;
|
||||
pub use number_entities::*;
|
||||
pub use select_option::*;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::{CellIdPB, CellIdParams};
|
||||
use crate::services::field::checklist_type_option::ChecklistTypeOption;
|
||||
use crate::services::field::{
|
||||
ChecklistTypeOption, MultiSelectTypeOption, SelectOption, SelectOptionColor,
|
||||
SingleSelectTypeOption,
|
||||
MultiSelectTypeOption, SelectOption, SelectOptionColor, SingleSelectTypeOption,
|
||||
};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
@ -287,34 +287,19 @@ impl From<MultiSelectTypeOptionPB> for MultiSelectTypeOption {
|
||||
#[derive(Clone, Debug, Default, ProtoBuf)]
|
||||
pub struct ChecklistTypeOptionPB {
|
||||
#[pb(index = 1)]
|
||||
pub options: Vec<SelectOptionPB>,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub disable_color: bool,
|
||||
pub config: String,
|
||||
}
|
||||
|
||||
impl From<ChecklistTypeOption> for ChecklistTypeOptionPB {
|
||||
fn from(data: ChecklistTypeOption) -> Self {
|
||||
fn from(_data: ChecklistTypeOption) -> Self {
|
||||
Self {
|
||||
options: data
|
||||
.options
|
||||
.into_iter()
|
||||
.map(|option| option.into())
|
||||
.collect(),
|
||||
disable_color: data.disable_color,
|
||||
config: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChecklistTypeOptionPB> for ChecklistTypeOption {
|
||||
fn from(data: ChecklistTypeOptionPB) -> Self {
|
||||
Self {
|
||||
options: data
|
||||
.options
|
||||
.into_iter()
|
||||
.map(|option| option.into())
|
||||
.collect(),
|
||||
disable_color: data.disable_color,
|
||||
}
|
||||
fn from(_data: ChecklistTypeOptionPB) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ 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,
|
||||
};
|
||||
@ -469,6 +470,38 @@ pub(crate) async fn update_select_option_cell_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn get_checklist_cell_data_handler(
|
||||
data: AFPluginData<CellIdPB>,
|
||||
manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||
) -> DataResult<ChecklistCellDataPB, FlowyError> {
|
||||
let params: CellIdParams = data.into_inner().try_into()?;
|
||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||
let data = database_editor
|
||||
.get_checklist_option(params.row_id, ¶ms.field_id)
|
||||
.await;
|
||||
data_result_ok(data)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn update_checklist_cell_handler(
|
||||
data: AFPluginData<ChecklistCellDataChangesetPB>,
|
||||
manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let params: ChecklistCellDataChangesetParams = data.into_inner().try_into()?;
|
||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||
let changeset = ChecklistCellChangeset {
|
||||
insert_options: params.insert_options,
|
||||
selected_option_ids: params.selected_option_ids,
|
||||
delete_option_ids: params.delete_option_ids,
|
||||
update_options: params.update_options,
|
||||
};
|
||||
database_editor
|
||||
.set_checklist_options(¶ms.view_id, params.row_id, ¶ms.field_id, changeset)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn update_date_cell_handler(
|
||||
data: AFPluginData<DateChangesetPB>,
|
||||
|
@ -44,6 +44,9 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
|
||||
.event(DatabaseEvent::DeleteSelectOption, delete_select_option_handler)
|
||||
.event(DatabaseEvent::GetSelectOptionCellData, get_select_option_handler)
|
||||
.event(DatabaseEvent::UpdateSelectOptionCell, update_select_option_cell_handler)
|
||||
// Checklist
|
||||
.event(DatabaseEvent::GetChecklistCellData, get_checklist_cell_data_handler)
|
||||
.event(DatabaseEvent::UpdateChecklistCell, update_checklist_cell_handler)
|
||||
// Date
|
||||
.event(DatabaseEvent::UpdateDateCell, update_date_cell_handler)
|
||||
// Group
|
||||
@ -227,6 +230,12 @@ pub enum DatabaseEvent {
|
||||
#[event(input = "SelectOptionCellChangesetPB")]
|
||||
UpdateSelectOptionCell = 72,
|
||||
|
||||
#[event(input = "CellIdPB", output = "ChecklistCellDataPB")]
|
||||
GetChecklistCellData = 73,
|
||||
|
||||
#[event(input = "ChecklistCellDataChangesetPB")]
|
||||
UpdateChecklistCell = 74,
|
||||
|
||||
/// [UpdateDateCell] event is used to update a date cell's data. [DateChangesetPB]
|
||||
/// contains the date and the time string. It can be cast to [CellChangesetPB] that
|
||||
/// will be used by the `update_cell` function.
|
||||
|
@ -9,43 +9,47 @@ use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellCache, CellProtobufBlob};
|
||||
use crate::services::field::checklist_type_option::ChecklistCellChangeset;
|
||||
use crate::services::field::*;
|
||||
use crate::services::group::make_no_status_group;
|
||||
|
||||
/// Decode the opaque cell data into readable format content
|
||||
pub trait CellDataDecoder: TypeOption {
|
||||
///
|
||||
/// Tries to decode the opaque cell string to `decoded_field_type`'s cell data. Sometimes, the `field_type`
|
||||
/// of the `FieldRevision` is not equal to the `decoded_field_type`(This happened When switching
|
||||
/// the field type of the `FieldRevision` to another field type). So the cell data is need to do
|
||||
/// Tries to decode the [Cell] to `decoded_field_type`'s cell data. Sometimes, the `field_type`
|
||||
/// of the `Field` is not equal to the `decoded_field_type`(This happened When switching
|
||||
/// the field type of the `Field` to another field type). So the cell data is need to do
|
||||
/// some transformation.
|
||||
///
|
||||
/// For example, the current field type of the `FieldRevision` is a checkbox. When switching the field
|
||||
/// For example, the current field type of the `Field` is a checkbox. When switching the field
|
||||
/// type from the checkbox to single select, it will create two new options,`Yes` and `No`, if they don't exist.
|
||||
/// But the data of the cell doesn't change. We can't iterate all the rows to transform the cell
|
||||
/// data that can be parsed by the current field type. One approach is to transform the cell data
|
||||
/// when it get read. For the moment, the cell data is a string, `Yes` or `No`. It needs to compare
|
||||
/// with the option's name, if match return the id of the option.
|
||||
fn decode_cell_str(
|
||||
/// when reading.
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
decoded_field_type: &FieldType,
|
||||
field: &Field,
|
||||
) -> FlowyResult<<Self as TypeOption>::CellData>;
|
||||
|
||||
/// Same as `decode_cell_data` does but Decode the cell data to readable `String`
|
||||
/// Decode the cell data to readable `String`
|
||||
/// For example, The string of the Multi-Select cell will be a list of the option's name
|
||||
/// separated by a comma.
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String;
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String;
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String;
|
||||
/// Same as [CellDataDecoder::stringify_cell_data] but the input parameter is the [Cell]
|
||||
fn stringify_cell(&self, cell: &Cell) -> String;
|
||||
}
|
||||
|
||||
pub trait CellDataChangeset: TypeOption {
|
||||
/// The changeset is able to parse into the concrete data struct if `TypeOption::CellChangeset`
|
||||
/// implements the `FromCellChangesetString` trait.
|
||||
/// For example,the SelectOptionCellChangeset,DateCellChangeset. etc.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `changeset`: the cell changeset that represents the changes of the cell.
|
||||
/// * `cell`: the data of the cell. It will be None if the cell does not contain any data.
|
||||
fn apply_changeset(
|
||||
&self,
|
||||
changeset: <Self as TypeOption>::CellChangeset,
|
||||
@ -109,13 +113,12 @@ pub fn get_cell_protobuf(
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cell_str`: the opaque cell string that can be decoded by corresponding structs that implement the
|
||||
/// `FromCellString` trait.
|
||||
/// * `cell`: the opaque cell string that can be decoded by corresponding structs.
|
||||
/// * `from_field_type`: the original field type of the passed-in cell data. Check the `TypeCellData`
|
||||
/// that is used to save the origin field type of the cell data.
|
||||
/// * `to_field_type`: decode the passed-in cell data to this field type. It will use the to_field_type's
|
||||
/// TypeOption to decode this cell data.
|
||||
/// * `field_rev`: used to get the corresponding TypeOption for the specified field type.
|
||||
/// * `field`: used to get the corresponding TypeOption for the specified field type.
|
||||
///
|
||||
/// returns: CellBytes
|
||||
///
|
||||
@ -154,11 +157,10 @@ pub fn try_decode_cell_to_cell_data<T: Default + 'static>(
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cell_str`: the opaque cell string that can be decoded by corresponding structs that implement the
|
||||
/// `FromCellString` trait.
|
||||
/// * `cell`: the opaque cell string that can be decoded by corresponding structs
|
||||
/// * `to_field_type`: the cell will be decoded to this field type's cell data.
|
||||
/// * `from_field_type`: the original field type of the passed-in cell data.
|
||||
/// * `field_rev`: used to get the corresponding TypeOption for the specified field type.
|
||||
/// * `field`: used to get the corresponding TypeOption for the specified field type.
|
||||
///
|
||||
/// returns: String
|
||||
pub fn stringify_cell_data(
|
||||
@ -223,6 +225,15 @@ pub fn insert_select_option_cell(option_ids: Vec<String>, field: &Field) -> Cell
|
||||
apply_cell_changeset(changeset, None, field, None).unwrap()
|
||||
}
|
||||
|
||||
pub fn insert_checklist_cell(insert_options: Vec<String>, field: &Field) -> Cell {
|
||||
let changeset = ChecklistCellChangeset {
|
||||
insert_options,
|
||||
..Default::default()
|
||||
}
|
||||
.to_cell_changeset_str();
|
||||
apply_cell_changeset(changeset, None, field, None).unwrap()
|
||||
}
|
||||
|
||||
pub fn delete_select_option_cell(option_ids: Vec<String>, field: &Field) -> Cell {
|
||||
let changeset =
|
||||
SelectOptionCellChangeset::from_delete_options(option_ids).to_cell_changeset_str();
|
||||
@ -434,4 +445,15 @@ impl<'a> CellBuilder<'a> {
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn insert_checklist_cell(&mut self, field_id: &str, option_names: Vec<String>) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
None => tracing::warn!("Can't find the field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self.cells.insert(
|
||||
field_id.to_owned(),
|
||||
insert_checklist_cell(option_names, field),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ use flowy_task::TaskDispatcher;
|
||||
use lib_infra::future::{to_fut, Fut};
|
||||
|
||||
use crate::entities::{
|
||||
CalendarEventPB, CellChangesetNotifyPB, CellPB, DatabaseFieldChangesetPB, DatabasePB,
|
||||
DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
|
||||
CalendarEventPB, CellChangesetNotifyPB, CellPB, ChecklistCellDataPB, DatabaseFieldChangesetPB,
|
||||
DatabasePB, DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
|
||||
FieldChangesetParams, FieldIdPB, FieldPB, FieldType, GroupPB, IndexFieldPB, InsertedRowPB,
|
||||
LayoutSettingParams, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangePB,
|
||||
SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams, UpdateSortParams,
|
||||
@ -28,6 +28,7 @@ use crate::services::cell::{
|
||||
};
|
||||
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,
|
||||
@ -552,6 +553,7 @@ impl DatabaseEditor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Just create an option for the field's type option. The option is save to the database.
|
||||
pub async fn create_select_option(
|
||||
&self,
|
||||
field_id: &str,
|
||||
@ -652,6 +654,33 @@ impl DatabaseEditor {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_checklist_option(&self, row_id: RowId, field_id: &str) -> ChecklistCellDataPB {
|
||||
let row_cell = self.database.lock().get_cell(field_id, &row_id);
|
||||
let cell_data = match row_cell {
|
||||
None => ChecklistCellData::default(),
|
||||
Some(row_cell) => ChecklistCellData::from(&row_cell.cell),
|
||||
};
|
||||
ChecklistCellDataPB::from(cell_data)
|
||||
}
|
||||
|
||||
pub async fn set_checklist_options(
|
||||
&self,
|
||||
view_id: &str,
|
||||
row_id: RowId,
|
||||
field_id: &str,
|
||||
changeset: ChecklistCellChangeset,
|
||||
) -> FlowyResult<()> {
|
||||
let field = self.database.lock().fields.get_field(field_id).ok_or(
|
||||
FlowyError::record_not_found().context(format!("Field with id:{} not found", &field_id)),
|
||||
)?;
|
||||
debug_assert!(FieldType::from(field.field_type).is_checklist());
|
||||
|
||||
self
|
||||
.update_cell_with_changeset(view_id, row_id, field_id, changeset)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn load_groups(&self, view_id: &str) -> FlowyResult<RepeatedGroupPB> {
|
||||
let view = self.database_views.get_view_editor(view_id).await?;
|
||||
|
@ -41,7 +41,7 @@ mod tests {
|
||||
) {
|
||||
assert_eq!(
|
||||
type_option
|
||||
.decode_cell_str(
|
||||
.decode_cell(
|
||||
&CheckboxCellData::from_cell_str(input_str).unwrap().into(),
|
||||
field_type,
|
||||
field
|
||||
|
@ -67,20 +67,20 @@ impl From<CheckboxTypeOption> for TypeOptionData {
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for CheckboxTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
cell_data
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(CheckboxCellData::from(cell))
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataDecoder for CheckboxTypeOption {
|
||||
fn decode_cell_str(
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
decoded_field_type: &FieldType,
|
||||
@ -90,14 +90,14 @@ impl CellDataDecoder for CheckboxTypeOption {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
self.decode_cell(cell)
|
||||
self.parse_cell(cell)
|
||||
}
|
||||
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
cell_data.to_string()
|
||||
}
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String {
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
Self::CellData::from(cell).to_string()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,207 @@
|
||||
use crate::entities::{ChecklistCellDataPB, ChecklistFilterPB, FieldType, SelectOptionPB};
|
||||
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData};
|
||||
use crate::services::field::{
|
||||
SelectOption, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare,
|
||||
TypeOptionCellDataFilter, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
|
||||
};
|
||||
use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use flowy_error::FlowyResult;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ChecklistTypeOption;
|
||||
|
||||
impl TypeOption for ChecklistTypeOption {
|
||||
type CellData = ChecklistCellData;
|
||||
type CellChangeset = ChecklistCellChangeset;
|
||||
type CellProtobufType = ChecklistCellDataPB;
|
||||
type CellFilter = ChecklistFilterPB;
|
||||
}
|
||||
|
||||
impl From<TypeOptionData> for ChecklistTypeOption {
|
||||
fn from(_data: TypeOptionData) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChecklistTypeOption> for TypeOptionData {
|
||||
fn from(_data: ChecklistTypeOption) -> Self {
|
||||
TypeOptionDataBuilder::new().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for ChecklistTypeOption {
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
let percentage = cell_data.percentage_complete();
|
||||
let selected_options = cell_data
|
||||
.options
|
||||
.iter()
|
||||
.filter(|option| cell_data.selected_option_ids.contains(&option.id))
|
||||
.map(|option| SelectOptionPB::from(option.clone()))
|
||||
.collect();
|
||||
|
||||
let options = cell_data
|
||||
.options
|
||||
.into_iter()
|
||||
.map(SelectOptionPB::from)
|
||||
.collect();
|
||||
|
||||
ChecklistCellDataPB {
|
||||
options,
|
||||
selected_options,
|
||||
percentage,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(ChecklistCellData::from(cell))
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataChangeset for ChecklistTypeOption {
|
||||
fn apply_changeset(
|
||||
&self,
|
||||
changeset: <Self as TypeOption>::CellChangeset,
|
||||
cell: Option<Cell>,
|
||||
) -> FlowyResult<(Cell, <Self as TypeOption>::CellData)> {
|
||||
match cell {
|
||||
Some(cell) => {
|
||||
let mut cell_data = self.parse_cell(&cell)?;
|
||||
update_cell_data_with_changeset(&mut cell_data, changeset);
|
||||
Ok((Cell::from(cell_data.clone()), cell_data))
|
||||
},
|
||||
None => {
|
||||
let cell_data = ChecklistCellData::from_options(changeset.insert_options);
|
||||
Ok((Cell::from(cell_data.clone()), cell_data))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update_cell_data_with_changeset(
|
||||
cell_data: &mut ChecklistCellData,
|
||||
mut changeset: ChecklistCellChangeset,
|
||||
) {
|
||||
// Delete the options
|
||||
cell_data
|
||||
.options
|
||||
.retain(|option| !changeset.delete_option_ids.contains(&option.id));
|
||||
cell_data
|
||||
.selected_option_ids
|
||||
.retain(|option_id| !changeset.delete_option_ids.contains(option_id));
|
||||
|
||||
// Insert new options
|
||||
changeset.insert_options.retain(|option_name| {
|
||||
!cell_data
|
||||
.options
|
||||
.iter()
|
||||
.any(|option| option.name == *option_name)
|
||||
});
|
||||
changeset
|
||||
.insert_options
|
||||
.into_iter()
|
||||
.for_each(|option_name| {
|
||||
let option = SelectOption::new(&option_name);
|
||||
cell_data.options.push(option.clone());
|
||||
});
|
||||
|
||||
// Update options
|
||||
changeset
|
||||
.update_options
|
||||
.into_iter()
|
||||
.for_each(|updated_option| {
|
||||
if let Some(option) = cell_data
|
||||
.options
|
||||
.iter_mut()
|
||||
.find(|option| option.id == updated_option.id)
|
||||
{
|
||||
option.name = updated_option.name;
|
||||
}
|
||||
});
|
||||
|
||||
// Select the options
|
||||
changeset
|
||||
.selected_option_ids
|
||||
.into_iter()
|
||||
.for_each(|option_id| {
|
||||
if let Some(index) = cell_data
|
||||
.selected_option_ids
|
||||
.iter()
|
||||
.position(|id| **id == option_id)
|
||||
{
|
||||
cell_data.selected_option_ids.remove(index);
|
||||
} else {
|
||||
cell_data.selected_option_ids.push(option_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl CellDataDecoder for ChecklistTypeOption {
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
decoded_field_type: &FieldType,
|
||||
_field: &Field,
|
||||
) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
if !decoded_field_type.is_checklist() {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
self.parse_cell(cell)
|
||||
}
|
||||
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
cell_data
|
||||
.selected_options()
|
||||
.into_iter()
|
||||
.map(|option| option.name)
|
||||
.collect::<Vec<_>>()
|
||||
.join(SELECTION_IDS_SEPARATOR)
|
||||
}
|
||||
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
let cell_data = self.parse_cell(cell).unwrap_or_default();
|
||||
self.stringify_cell_data(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellDataFilter for ChecklistTypeOption {
|
||||
fn apply_filter(
|
||||
&self,
|
||||
filter: &<Self as TypeOption>::CellFilter,
|
||||
field_type: &FieldType,
|
||||
cell_data: &<Self as TypeOption>::CellData,
|
||||
) -> bool {
|
||||
if !field_type.is_checklist() {
|
||||
return true;
|
||||
}
|
||||
let selected_options = cell_data.selected_options();
|
||||
filter.is_visible(&cell_data.options, &selected_options)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellDataCompare for ChecklistTypeOption {
|
||||
fn apply_cmp(
|
||||
&self,
|
||||
cell_data: &<Self as TypeOption>::CellData,
|
||||
other_cell_data: &<Self as TypeOption>::CellData,
|
||||
) -> Ordering {
|
||||
let left = cell_data.percentage_complete();
|
||||
let right = other_cell_data.percentage_complete();
|
||||
if left > right {
|
||||
Ordering::Greater
|
||||
} else if left < right {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionTransform for ChecklistTypeOption {}
|
@ -0,0 +1,107 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{FromCellChangeset, ToCellChangeset};
|
||||
use crate::services::field::{SelectOption, CELL_DATA};
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::rows::{new_cell_builder, Cell};
|
||||
use flowy_error::{internal_error, FlowyResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ChecklistCellData {
|
||||
pub options: Vec<SelectOption>,
|
||||
pub selected_option_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl ToString for ChecklistCellData {
|
||||
fn to_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl ChecklistCellData {
|
||||
pub fn selected_options(&self) -> Vec<SelectOption> {
|
||||
self
|
||||
.options
|
||||
.iter()
|
||||
.filter(|option| self.selected_option_ids.contains(&option.id))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn percentage_complete(&self) -> f64 {
|
||||
let selected_options = self.selected_option_ids.len();
|
||||
let total_options = self.options.len();
|
||||
|
||||
if total_options == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
(selected_options as f64) / (total_options as f64)
|
||||
}
|
||||
|
||||
pub fn from_options(options: Vec<String>) -> Self {
|
||||
let options = options
|
||||
.into_iter()
|
||||
.map(|option_name| SelectOption::new(&option_name))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
options,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cell> for ChecklistCellData {
|
||||
fn from(cell: &Cell) -> Self {
|
||||
cell
|
||||
.get_str_value(CELL_DATA)
|
||||
.map(|data| serde_json::from_str::<ChecklistCellData>(&data).unwrap_or_default())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChecklistCellData> for Cell {
|
||||
fn from(cell_data: ChecklistCellData) -> Self {
|
||||
let data = serde_json::to_string(&cell_data).unwrap_or_default();
|
||||
new_cell_builder(FieldType::Checklist)
|
||||
.insert_str_value(CELL_DATA, data)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ChecklistCellChangeset {
|
||||
/// List of option names that will be inserted
|
||||
pub insert_options: Vec<String>,
|
||||
pub selected_option_ids: Vec<String>,
|
||||
pub delete_option_ids: Vec<String>,
|
||||
pub update_options: Vec<SelectOption>,
|
||||
}
|
||||
|
||||
impl FromCellChangeset for ChecklistCellChangeset {
|
||||
fn from_changeset(changeset: String) -> FlowyResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
serde_json::from_str::<ChecklistCellChangeset>(&changeset).map_err(internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCellChangeset for ChecklistCellChangeset {
|
||||
fn to_cell_changeset_str(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test() {
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
|
||||
let c = (a as f32) / (b as f32);
|
||||
println!("{}", c);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
mod checklist;
|
||||
mod checklist_entities;
|
||||
|
||||
pub use checklist::*;
|
||||
pub use checklist_entities::*;
|
@ -506,9 +506,9 @@ mod tests {
|
||||
field: &Field,
|
||||
) -> String {
|
||||
let decoded_data = type_option
|
||||
.decode_cell_str(cell, &FieldType::DateTime, field)
|
||||
.decode_cell(cell, &FieldType::DateTime, field)
|
||||
.unwrap();
|
||||
let decoded_data = type_option.convert_to_protobuf(decoded_data);
|
||||
let decoded_data = type_option.protobuf_encode(decoded_data);
|
||||
if include_time {
|
||||
format!("{} {}", decoded_data.date, decoded_data.time)
|
||||
.trim_end()
|
||||
|
@ -66,14 +66,14 @@ impl From<DateTypeOption> for TypeOptionData {
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for DateTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
self.today_desc_from_timestamp(cell_data)
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(DateCellData::from(cell))
|
||||
}
|
||||
}
|
||||
@ -156,7 +156,7 @@ impl DateTypeOption {
|
||||
impl TypeOptionTransform for DateTypeOption {}
|
||||
|
||||
impl CellDataDecoder for DateTypeOption {
|
||||
fn decode_cell_str(
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
decoded_field_type: &FieldType,
|
||||
@ -170,16 +170,16 @@ impl CellDataDecoder for DateTypeOption {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
self.decode_cell(cell)
|
||||
self.parse_cell(cell)
|
||||
}
|
||||
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
self.today_desc_from_timestamp(cell_data).date
|
||||
}
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String {
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
let cell_data = Self::CellData::from(cell);
|
||||
self.decode_cell_data_to_str(cell_data)
|
||||
self.stringify_cell_data(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod checkbox_type_option;
|
||||
pub mod checklist_type_option;
|
||||
pub mod date_type_option;
|
||||
pub mod number_type_option;
|
||||
pub mod selection_type_option;
|
||||
|
@ -89,7 +89,7 @@ mod tests {
|
||||
) {
|
||||
assert_eq!(
|
||||
type_option
|
||||
.decode_cell_str(
|
||||
.decode_cell(
|
||||
&NumberCellData(input_str.to_owned()).into(),
|
||||
field_type,
|
||||
field
|
||||
|
@ -96,14 +96,14 @@ impl From<NumberTypeOption> for TypeOptionData {
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for NumberTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
ProtobufStr::from(cell_data.0)
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(NumberCellData::from(cell))
|
||||
}
|
||||
}
|
||||
@ -171,7 +171,7 @@ impl NumberTypeOption {
|
||||
impl TypeOptionTransform for NumberTypeOption {}
|
||||
|
||||
impl CellDataDecoder for NumberTypeOption {
|
||||
fn decode_cell_str(
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
decoded_field_type: &FieldType,
|
||||
@ -181,22 +181,22 @@ impl CellDataDecoder for NumberTypeOption {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
let num_cell_data = self.decode_cell(cell)?;
|
||||
let num_cell_data = self.parse_cell(cell)?;
|
||||
Ok(NumberCellData::from(
|
||||
self.format_cell_data(&num_cell_data)?.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
match self.format_cell_data(&cell_data) {
|
||||
Ok(cell_data) => cell_data.to_string(),
|
||||
Err(_) => "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String {
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
let cell_data = Self::CellData::from(cell);
|
||||
self.decode_cell_data_to_str(cell_data)
|
||||
self.stringify_cell_data(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB};
|
||||
use crate::services::field::{SelectOption, SelectedSelectOptions};
|
||||
use crate::services::field::SelectOption;
|
||||
|
||||
impl ChecklistFilterPB {
|
||||
pub fn is_visible(
|
||||
&self,
|
||||
all_options: &[SelectOption],
|
||||
selected_options: &SelectedSelectOptions,
|
||||
selected_options: &[SelectOption],
|
||||
) -> bool {
|
||||
let selected_option_ids = selected_options
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| option.id.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
|
@ -1,143 +0,0 @@
|
||||
use crate::entities::{ChecklistFilterPB, FieldType, SelectOptionCellDataPB};
|
||||
use crate::services::cell::CellDataChangeset;
|
||||
use crate::services::field::{
|
||||
SelectOption, SelectOptionCellChangeset, SelectOptionIds, SelectTypeOptionSharedAction,
|
||||
SelectedSelectOptions, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare,
|
||||
TypeOptionCellDataFilter,
|
||||
};
|
||||
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use flowy_error::FlowyResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// Multiple select
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ChecklistTypeOption {
|
||||
pub options: Vec<SelectOption>,
|
||||
pub disable_color: bool,
|
||||
}
|
||||
|
||||
impl TypeOption for ChecklistTypeOption {
|
||||
type CellData = SelectOptionIds;
|
||||
type CellChangeset = SelectOptionCellChangeset;
|
||||
type CellProtobufType = SelectOptionCellDataPB;
|
||||
type CellFilter = ChecklistFilterPB;
|
||||
}
|
||||
|
||||
impl From<TypeOptionData> for ChecklistTypeOption {
|
||||
fn from(data: TypeOptionData) -> Self {
|
||||
data
|
||||
.get_str_value("content")
|
||||
.map(|s| serde_json::from_str::<ChecklistTypeOption>(&s).unwrap_or_default())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChecklistTypeOption> for TypeOptionData {
|
||||
fn from(data: ChecklistTypeOption) -> Self {
|
||||
let content = serde_json::to_string(&data).unwrap_or_default();
|
||||
TypeOptionDataBuilder::new()
|
||||
.insert_str_value("content", content)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for ChecklistTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
self.get_selected_options(cell_data).into()
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(SelectOptionIds::from(cell))
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectTypeOptionSharedAction for ChecklistTypeOption {
|
||||
fn number_of_max_options(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
fn to_type_option_data(&self) -> TypeOptionData {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
fn options(&self) -> &Vec<SelectOption> {
|
||||
&self.options
|
||||
}
|
||||
|
||||
fn mut_options(&mut self) -> &mut Vec<SelectOption> {
|
||||
&mut self.options
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataChangeset for ChecklistTypeOption {
|
||||
fn apply_changeset(
|
||||
&self,
|
||||
changeset: <Self as TypeOption>::CellChangeset,
|
||||
cell: Option<Cell>,
|
||||
) -> FlowyResult<(Cell, <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>>();
|
||||
|
||||
let select_option_ids = match cell {
|
||||
None => SelectOptionIds::from(insert_option_ids),
|
||||
Some(cell) => {
|
||||
let mut select_ids = SelectOptionIds::from(&cell);
|
||||
for insert_option_id in insert_option_ids {
|
||||
if !select_ids.contains(&insert_option_id) {
|
||||
select_ids.push(insert_option_id);
|
||||
}
|
||||
}
|
||||
|
||||
for delete_option_id in changeset.delete_option_ids {
|
||||
select_ids.retain(|id| id != &delete_option_id);
|
||||
}
|
||||
|
||||
select_ids
|
||||
},
|
||||
};
|
||||
Ok((
|
||||
select_option_ids.to_cell_data(FieldType::Checklist),
|
||||
select_option_ids,
|
||||
))
|
||||
}
|
||||
}
|
||||
impl TypeOptionCellDataFilter for ChecklistTypeOption {
|
||||
fn apply_filter(
|
||||
&self,
|
||||
filter: &<Self as TypeOption>::CellFilter,
|
||||
field_type: &FieldType,
|
||||
cell_data: &<Self as TypeOption>::CellData,
|
||||
) -> bool {
|
||||
if !field_type.is_check_list() {
|
||||
return true;
|
||||
}
|
||||
let selected_options =
|
||||
SelectedSelectOptions::from(self.get_selected_options(cell_data.clone()));
|
||||
filter.is_visible(&self.options, &selected_options)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOptionCellDataCompare for ChecklistTypeOption {
|
||||
fn apply_cmp(
|
||||
&self,
|
||||
cell_data: &<Self as TypeOption>::CellData,
|
||||
other_cell_data: &<Self as TypeOption>::CellData,
|
||||
) -> Ordering {
|
||||
cell_data.len().cmp(&other_cell_data.len())
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
mod checklist_filter;
|
||||
mod checklist_type_option;
|
||||
mod multi_select_type_option;
|
||||
mod select_filter;
|
||||
mod select_ids;
|
||||
@ -9,7 +8,6 @@ mod single_select_type_option;
|
||||
mod type_option_transform;
|
||||
|
||||
pub use checklist_filter::*;
|
||||
pub use checklist_type_option::*;
|
||||
pub use multi_select_type_option::*;
|
||||
pub use select_ids::*;
|
||||
pub use select_option::*;
|
||||
|
@ -11,8 +11,8 @@ use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB};
|
||||
use crate::services::cell::CellDataChangeset;
|
||||
use crate::services::field::{
|
||||
default_order, SelectOption, SelectOptionCellChangeset, SelectOptionIds,
|
||||
SelectTypeOptionSharedAction, SelectedSelectOptions, TypeOption, TypeOptionCellData,
|
||||
TypeOptionCellDataCompare, TypeOptionCellDataFilter,
|
||||
SelectTypeOptionSharedAction, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare,
|
||||
TypeOptionCellDataFilter,
|
||||
};
|
||||
|
||||
// Multiple select
|
||||
@ -48,14 +48,14 @@ impl From<MultiSelectTypeOption> for TypeOptionData {
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for MultiSelectTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
self.get_selected_options(cell_data).into()
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(SelectOptionIds::from(cell))
|
||||
}
|
||||
}
|
||||
@ -130,8 +130,7 @@ impl TypeOptionCellDataFilter for MultiSelectTypeOption {
|
||||
if !field_type.is_multi_select() {
|
||||
return true;
|
||||
}
|
||||
let selected_options =
|
||||
SelectedSelectOptions::from(self.get_selected_options(cell_data.clone()));
|
||||
let selected_options = self.get_selected_options(cell_data.clone()).select_options;
|
||||
filter.is_visible(&selected_options, FieldType::MultiSelect)
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,12 @@
|
||||
#![allow(clippy::needless_collect)]
|
||||
|
||||
use crate::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
|
||||
use crate::services::field::SelectedSelectOptions;
|
||||
use crate::services::field::SelectOption;
|
||||
|
||||
impl SelectOptionFilterPB {
|
||||
pub fn is_visible(
|
||||
&self,
|
||||
selected_options: &SelectedSelectOptions,
|
||||
field_type: FieldType,
|
||||
) -> bool {
|
||||
let selected_option_ids: Vec<&String> = selected_options
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| &option.id)
|
||||
.collect();
|
||||
pub fn is_visible(&self, selected_options: &[SelectOption], field_type: FieldType) -> bool {
|
||||
let selected_option_ids: Vec<&String> =
|
||||
selected_options.iter().map(|option| &option.id).collect();
|
||||
match self.condition {
|
||||
SelectOptionConditionPB::OptionIs => match field_type {
|
||||
FieldType::SingleSelect => {
|
||||
@ -21,7 +14,7 @@ impl SelectOptionFilterPB {
|
||||
return true;
|
||||
}
|
||||
|
||||
if selected_options.options.is_empty() {
|
||||
if selected_options.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -54,7 +47,7 @@ impl SelectOptionFilterPB {
|
||||
return true;
|
||||
}
|
||||
|
||||
if selected_options.options.is_empty() {
|
||||
if selected_options.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -87,7 +80,6 @@ impl SelectOptionFilterPB {
|
||||
mod tests {
|
||||
#![allow(clippy::all)]
|
||||
use crate::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
|
||||
use crate::services::field::selection_type_option::SelectedSelectOptions;
|
||||
use crate::services::field::SelectOption;
|
||||
|
||||
#[test]
|
||||
@ -98,37 +90,15 @@ mod tests {
|
||||
option_ids: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(filter.is_visible(&vec![], FieldType::SingleSelect), true);
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions { options: vec![] },
|
||||
FieldType::SingleSelect
|
||||
),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions {
|
||||
options: vec![option.clone()]
|
||||
},
|
||||
FieldType::SingleSelect
|
||||
),
|
||||
filter.is_visible(&vec![option.clone()], FieldType::SingleSelect),
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(filter.is_visible(&vec![], FieldType::MultiSelect), true);
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions { options: vec![] },
|
||||
FieldType::MultiSelect
|
||||
),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions {
|
||||
options: vec![option]
|
||||
},
|
||||
FieldType::MultiSelect
|
||||
),
|
||||
filter.is_visible(&vec![option], FieldType::MultiSelect),
|
||||
false,
|
||||
);
|
||||
}
|
||||
@ -143,38 +113,16 @@ mod tests {
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions {
|
||||
options: vec![option_1.clone()]
|
||||
},
|
||||
FieldType::SingleSelect
|
||||
),
|
||||
filter.is_visible(&vec![option_1.clone()], FieldType::SingleSelect),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions { options: vec![] },
|
||||
FieldType::SingleSelect
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert_eq!(filter.is_visible(&vec![], FieldType::SingleSelect), false,);
|
||||
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions {
|
||||
options: vec![option_1.clone()]
|
||||
},
|
||||
FieldType::MultiSelect
|
||||
),
|
||||
filter.is_visible(&vec![option_1.clone()], FieldType::MultiSelect),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
filter.is_visible(
|
||||
&SelectedSelectOptions { options: vec![] },
|
||||
FieldType::MultiSelect
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert_eq!(filter.is_visible(&vec![], FieldType::MultiSelect), false,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -194,7 +142,7 @@ mod tests {
|
||||
(vec![option_1.clone(), option_2.clone()], false),
|
||||
] {
|
||||
assert_eq!(
|
||||
filter.is_visible(&SelectedSelectOptions { options }, FieldType::SingleSelect),
|
||||
filter.is_visible(&options, FieldType::SingleSelect),
|
||||
is_visible
|
||||
);
|
||||
}
|
||||
@ -217,7 +165,7 @@ mod tests {
|
||||
(vec![option_1.clone(), option_2.clone()], true),
|
||||
] {
|
||||
assert_eq!(
|
||||
filter.is_visible(&SelectedSelectOptions { options }, FieldType::SingleSelect),
|
||||
filter.is_visible(&options, FieldType::SingleSelect),
|
||||
is_visible
|
||||
);
|
||||
}
|
||||
@ -238,7 +186,7 @@ mod tests {
|
||||
(vec![option_1.clone(), option_2.clone()], true),
|
||||
] {
|
||||
assert_eq!(
|
||||
filter.is_visible(&SelectedSelectOptions { options }, FieldType::SingleSelect),
|
||||
filter.is_visible(&options, FieldType::SingleSelect),
|
||||
is_visible
|
||||
);
|
||||
}
|
||||
@ -266,7 +214,7 @@ mod tests {
|
||||
(vec![], true),
|
||||
] {
|
||||
assert_eq!(
|
||||
filter.is_visible(&SelectedSelectOptions { options }, FieldType::MultiSelect),
|
||||
filter.is_visible(&options, FieldType::MultiSelect),
|
||||
is_visible
|
||||
);
|
||||
}
|
||||
@ -292,7 +240,7 @@ mod tests {
|
||||
(vec![option_3.clone()], false),
|
||||
] {
|
||||
assert_eq!(
|
||||
filter.is_visible(&SelectedSelectOptions { options }, FieldType::MultiSelect),
|
||||
filter.is_visible(&options, FieldType::MultiSelect),
|
||||
is_visible
|
||||
);
|
||||
}
|
||||
@ -308,7 +256,7 @@ mod tests {
|
||||
};
|
||||
for (options, is_visible) in vec![(vec![option_1.clone()], true), (vec![], true)] {
|
||||
assert_eq!(
|
||||
filter.is_visible(&SelectedSelectOptions { options }, FieldType::MultiSelect),
|
||||
filter.is_visible(&options, FieldType::MultiSelect),
|
||||
is_visible
|
||||
);
|
||||
}
|
||||
|
@ -83,15 +83,3 @@ pub fn make_selected_options(ids: SelectOptionIds, options: &[SelectOption]) ->
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub struct SelectedSelectOptions {
|
||||
pub(crate) options: Vec<SelectOption>,
|
||||
}
|
||||
|
||||
impl std::convert::From<SelectOptionCellData> for SelectedSelectOptions {
|
||||
fn from(data: SelectOptionCellData) -> Self {
|
||||
Self {
|
||||
options: data.select_options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,12 @@ use crate::entities::{FieldType, SelectOptionCellDataPB};
|
||||
use crate::services::cell::{
|
||||
CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, ToCellChangeset,
|
||||
};
|
||||
|
||||
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
|
||||
use crate::services::field::{
|
||||
make_selected_options, CheckboxCellData, ChecklistTypeOption, MultiSelectTypeOption,
|
||||
SelectOption, SelectOptionCellData, SelectOptionColor, SelectOptionIds, SingleSelectTypeOption,
|
||||
TypeOption, TypeOptionCellData, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
|
||||
make_selected_options, CheckboxCellData, MultiSelectTypeOption, SelectOption,
|
||||
SelectOptionCellData, SelectOptionColor, SelectOptionIds, SingleSelectTypeOption, TypeOption,
|
||||
TypeOptionCellData, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
|
||||
};
|
||||
|
||||
/// Defines the shared actions used by SingleSelect or Multi-Select.
|
||||
@ -124,16 +125,16 @@ impl<T> CellDataDecoder for T
|
||||
where
|
||||
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + TypeOptionCellData,
|
||||
{
|
||||
fn decode_cell_str(
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field: &Field,
|
||||
) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
self.decode_cell(cell)
|
||||
self.parse_cell(cell)
|
||||
}
|
||||
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
self
|
||||
.get_selected_options(cell_data)
|
||||
.select_options
|
||||
@ -143,35 +144,29 @@ where
|
||||
.join(SELECTION_IDS_SEPARATOR)
|
||||
}
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String {
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
let cell_data = Self::CellData::from(cell);
|
||||
self.decode_cell_data_to_str(cell_data)
|
||||
self.stringify_cell_data(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_type_option_from_field(
|
||||
field_rev: &Field,
|
||||
field: &Field,
|
||||
) -> FlowyResult<Box<dyn SelectTypeOptionSharedAction>> {
|
||||
let field_type = FieldType::from(field_rev.field_type);
|
||||
let field_type = FieldType::from(field.field_type);
|
||||
match &field_type {
|
||||
FieldType::SingleSelect => {
|
||||
let type_option = field_rev
|
||||
let type_option = field
|
||||
.get_type_option::<SingleSelectTypeOption>(field_type)
|
||||
.unwrap_or_default();
|
||||
Ok(Box::new(type_option))
|
||||
},
|
||||
FieldType::MultiSelect => {
|
||||
let type_option = field_rev
|
||||
let type_option = field
|
||||
.get_type_option::<MultiSelectTypeOption>(&field_type)
|
||||
.unwrap_or_default();
|
||||
Ok(Box::new(type_option))
|
||||
},
|
||||
FieldType::Checklist => {
|
||||
let type_option = field_rev
|
||||
.get_type_option::<ChecklistTypeOption>(&field_type)
|
||||
.unwrap_or_default();
|
||||
Ok(Box::new(type_option))
|
||||
},
|
||||
ty => {
|
||||
tracing::error!("Unsupported field type: {:?} for this handler", ty);
|
||||
Err(ErrorCode::FieldInvalidOperation.into())
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB};
|
||||
use crate::services::cell::CellDataChangeset;
|
||||
use crate::services::field::{
|
||||
default_order, SelectOption, SelectedSelectOptions, TypeOption, TypeOptionCellData,
|
||||
TypeOptionCellDataCompare, TypeOptionCellDataFilter,
|
||||
default_order, SelectOption, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare,
|
||||
TypeOptionCellDataFilter,
|
||||
};
|
||||
use crate::services::field::{
|
||||
SelectOptionCellChangeset, SelectOptionIds, SelectTypeOptionSharedAction,
|
||||
@ -47,14 +47,14 @@ impl From<SingleSelectTypeOption> for TypeOptionData {
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for SingleSelectTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
self.get_selected_options(cell_data).into()
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(SelectOptionIds::from(cell))
|
||||
}
|
||||
}
|
||||
@ -121,8 +121,7 @@ impl TypeOptionCellDataFilter for SingleSelectTypeOption {
|
||||
if !field_type.is_single_select() {
|
||||
return true;
|
||||
}
|
||||
let selected_options =
|
||||
SelectedSelectOptions::from(self.get_selected_options(cell_data.clone()));
|
||||
let selected_options = self.get_selected_options(cell_data.clone()).select_options;
|
||||
filter.is_visible(&selected_options, FieldType::SingleSelect)
|
||||
}
|
||||
}
|
||||
|
@ -86,20 +86,20 @@ impl TypeOptionTransform for RichTextTypeOption {
|
||||
}
|
||||
|
||||
impl TypeOptionCellData for RichTextTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
ProtobufStr::from(cell_data.0)
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(StrCellData::from(cell))
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataDecoder for RichTextTypeOption {
|
||||
fn decode_cell_str(
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
_decoded_field_type: &FieldType,
|
||||
@ -108,11 +108,11 @@ impl CellDataDecoder for RichTextTypeOption {
|
||||
Ok(StrCellData::from(cell))
|
||||
}
|
||||
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
cell_data.to_string()
|
||||
}
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String {
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
Self::CellData::from(cell).to_string()
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,10 @@ use crate::entities::{
|
||||
URLTypeOptionPB,
|
||||
};
|
||||
use crate::services::cell::{CellDataDecoder, FromCellChangeset, ToCellChangeset};
|
||||
use crate::services::field::checklist_type_option::ChecklistTypeOption;
|
||||
use crate::services::field::{
|
||||
CheckboxTypeOption, ChecklistTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption,
|
||||
RichTextTypeOption, SingleSelectTypeOption, URLTypeOption,
|
||||
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
|
||||
SingleSelectTypeOption, URLTypeOption,
|
||||
};
|
||||
use crate::services::filter::FromFilterString;
|
||||
|
||||
@ -53,20 +54,19 @@ pub trait TypeOption {
|
||||
}
|
||||
|
||||
pub trait TypeOptionCellData: TypeOption {
|
||||
/// Convert the decoded cell data into corresponding `Protobuf struct`.
|
||||
/// Encode the cell data into corresponding `Protobuf struct`.
|
||||
/// For example:
|
||||
/// FieldType::URL => URLCellDataPB
|
||||
/// FieldType::Date=> DateCellDataPB
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType;
|
||||
|
||||
/// Decodes the opaque cell string to corresponding data struct.
|
||||
// For example, the cell data is timestamp if its field type is `FieldType::Date`. This cell
|
||||
// data can not directly show to user. So it needs to be encode as the date string with custom
|
||||
// format setting. Encode `1647251762` to `"Mar 14,2022`
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData>;
|
||||
/// Parse the opaque [Cell] to corresponding data struct.
|
||||
/// The [Cell] is a map that stores list of key/value data. Each [TypeOption::CellData]
|
||||
/// should implement the From<&Cell> trait to parse the [Cell] to corresponding data struct.
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData>;
|
||||
}
|
||||
|
||||
pub trait TypeOptionTransform: TypeOption {
|
||||
|
@ -13,10 +13,11 @@ use crate::services::cell::{
|
||||
CellCache, CellDataChangeset, CellDataDecoder, CellFilterCache, CellProtobufBlob,
|
||||
FromCellChangeset,
|
||||
};
|
||||
use crate::services::field::checklist_type_option::ChecklistTypeOption;
|
||||
use crate::services::field::{
|
||||
CheckboxTypeOption, ChecklistTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption,
|
||||
RichTextTypeOption, SingleSelectTypeOption, TypeOption, TypeOptionCellData,
|
||||
TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionTransform, URLTypeOption,
|
||||
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
|
||||
SingleSelectTypeOption, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare,
|
||||
TypeOptionCellDataFilter, TypeOptionTransform, URLTypeOption,
|
||||
};
|
||||
|
||||
pub const CELL_DATA: &str = "data";
|
||||
@ -36,6 +37,7 @@ pub trait TypeOptionCellDataHandler: Send + Sync + 'static {
|
||||
field_rev: &Field,
|
||||
) -> FlowyResult<CellProtobufBlob>;
|
||||
|
||||
// TODO(nathan): replace cell_changeset with BoxAny to get rid of the serde process.
|
||||
fn handle_cell_changeset(
|
||||
&self,
|
||||
cell_changeset: String,
|
||||
@ -141,7 +143,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let cell_data = self.decode_cell_str(cell, decoded_field_type, field)?;
|
||||
let cell_data = self.decode_cell(cell, decoded_field_type, field)?;
|
||||
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
|
||||
// tracing::trace!(
|
||||
// "Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
|
||||
@ -217,7 +219,7 @@ where
|
||||
.get_cell_data(cell, decoded_field_type, field_rev)?
|
||||
.unbox_or_default::<<Self as TypeOption>::CellData>();
|
||||
|
||||
CellProtobufBlob::from(self.convert_to_protobuf(cell_data))
|
||||
CellProtobufBlob::from(self.protobuf_encode(cell_data))
|
||||
}
|
||||
|
||||
fn handle_cell_changeset(
|
||||
@ -265,10 +267,10 @@ where
|
||||
if self.transformable() {
|
||||
let cell_data = self.transform_type_option_cell(cell, field_type, field);
|
||||
if let Some(cell_data) = cell_data {
|
||||
return self.decode_cell_data_to_str(cell_data);
|
||||
return self.stringify_cell_data(cell_data);
|
||||
}
|
||||
}
|
||||
self.decode_cell_to_str(cell)
|
||||
self.stringify_cell(cell)
|
||||
}
|
||||
|
||||
fn get_cell_data(
|
||||
|
@ -47,20 +47,20 @@ impl From<URLTypeOption> for TypeOptionData {
|
||||
impl TypeOptionTransform for URLTypeOption {}
|
||||
|
||||
impl TypeOptionCellData for URLTypeOption {
|
||||
fn convert_to_protobuf(
|
||||
fn protobuf_encode(
|
||||
&self,
|
||||
cell_data: <Self as TypeOption>::CellData,
|
||||
) -> <Self as TypeOption>::CellProtobufType {
|
||||
cell_data.into()
|
||||
}
|
||||
|
||||
fn decode_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
|
||||
Ok(URLCellData::from(cell))
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataDecoder for URLTypeOption {
|
||||
fn decode_cell_str(
|
||||
fn decode_cell(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
decoded_field_type: &FieldType,
|
||||
@ -70,16 +70,16 @@ impl CellDataDecoder for URLTypeOption {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
self.decode_cell(cell)
|
||||
self.parse_cell(cell)
|
||||
}
|
||||
|
||||
fn decode_cell_data_to_str(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
cell_data.data
|
||||
}
|
||||
|
||||
fn decode_cell_to_str(&self, cell: &Cell) -> String {
|
||||
fn stringify_cell(&self, cell: &Cell) -> String {
|
||||
let cell_data = Self::CellData::from(cell);
|
||||
self.decode_cell_data_to_str(cell_data)
|
||||
self.stringify_cell_data(cell_data)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user