refactor: checkbox field type (#4682)

This commit is contained in:
Richard Shiue 2024-02-20 14:02:32 +08:00 committed by GitHub
parent a6c6aa819c
commit 334aedd6c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 121 additions and 140 deletions

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -86,7 +87,6 @@ class CheckboxCellState with _$CheckboxCellState {
} }
} }
bool _isSelected(String? cellData) { bool _isSelected(CheckboxCellDataPB? cellData) {
// The backend use "Yes" and "No" to represent the checkbox cell data. return cellData != null && cellData.isChecked;
return cellData == "Yes";
} }

View File

@ -6,7 +6,7 @@ import 'cell_data_loader.dart';
import 'cell_data_persistence.dart'; import 'cell_data_persistence.dart';
typedef TextCellController = CellController<String, String>; typedef TextCellController = CellController<String, String>;
typedef CheckboxCellController = CellController<String, String>; typedef CheckboxCellController = CellController<CheckboxCellDataPB, String>;
typedef NumberCellController = CellController<String, String>; typedef NumberCellController = CellController<String, String>;
typedef SelectOptionCellController typedef SelectOptionCellController
= CellController<SelectOptionCellDataPB, String>; = CellController<SelectOptionCellDataPB, String>;
@ -24,13 +24,13 @@ CellController makeCellController(
final fieldType = fieldController.getField(cellContext.fieldId)!.fieldType; final fieldType = fieldController.getField(cellContext.fieldId)!.fieldType;
switch (fieldType) { switch (fieldType) {
case FieldType.Checkbox: case FieldType.Checkbox:
return TextCellController( return CheckboxCellController(
viewId: viewId, viewId: viewId,
fieldController: fieldController, fieldController: fieldController,
cellContext: cellContext, cellContext: cellContext,
rowCache: rowCache, rowCache: rowCache,
cellDataLoader: CellDataLoader( cellDataLoader: CellDataLoader(
parser: StringCellDataParser(), parser: CheckboxCellDataParser(),
), ),
cellDataPersistence: TextCellDataPersistence(), cellDataPersistence: TextCellDataPersistence(),
); );

View File

@ -66,6 +66,16 @@ class StringCellDataParser implements CellDataParser<String> {
} }
} }
class CheckboxCellDataParser implements CellDataParser<CheckboxCellDataPB> {
@override
CheckboxCellDataPB? parserData(List<int> data) {
if (data.isEmpty) {
return null;
}
return CheckboxCellDataPB.fromBuffer(data);
}
}
class NumberCellDataParser implements CellDataParser<String> { class NumberCellDataParser implements CellDataParser<String> {
@override @override
String? parserData(List<int> data) { String? parserData(List<int> data) {

View File

@ -1,5 +1,6 @@
import { import {
CellPB, CellPB,
CheckboxCellDataPB,
ChecklistCellDataPB, ChecklistCellDataPB,
DateCellDataPB, DateCellDataPB,
FieldType, FieldType,
@ -28,7 +29,7 @@ export interface NumberCell extends Cell {
export interface CheckboxCell extends Cell { export interface CheckboxCell extends Cell {
fieldType: FieldType.Checkbox; fieldType: FieldType.Checkbox;
data: 'Yes' | 'No'; data: boolean;
} }
export interface UrlCell extends Cell { export interface UrlCell extends Cell {
@ -99,6 +100,10 @@ export type UndeterminedCell =
| UrlCell | UrlCell
| ChecklistCell; | ChecklistCell;
const pbToCheckboxCellData = (pb: CheckboxCellDataPB): boolean => (
pb.is_checked
);
const pbToDateTimeCellData = (pb: DateCellDataPB): DateTimeCellData => ({ const pbToDateTimeCellData = (pb: DateCellDataPB): DateTimeCellData => ({
date: pb.date, date: pb.date,
time: pb.time, time: pb.time,
@ -136,8 +141,9 @@ function bytesToCellData(bytes: Uint8Array, fieldType: FieldType) {
switch (fieldType) { switch (fieldType) {
case FieldType.RichText: case FieldType.RichText:
case FieldType.Number: case FieldType.Number:
case FieldType.Checkbox:
return new TextDecoder().decode(bytes); return new TextDecoder().decode(bytes);
case FieldType.Checkbox:
return pbToCheckboxCellData(CheckboxCellDataPB.deserialize(bytes));
case FieldType.DateTime: case FieldType.DateTime:
return pbToDateTimeCellData(DateCellDataPB.deserialize(bytes)); return pbToDateTimeCellData(DateCellDataPB.deserialize(bytes));
case FieldType.LastEditedTime: case FieldType.LastEditedTime:

View File

@ -98,7 +98,7 @@ function pbToSelectTypeOption(pb: SingleSelectTypeOptionPB | MultiSelectTypeOpti
function pbToCheckboxTypeOption(pb: CheckboxTypeOptionPB): CheckboxTypeOption { function pbToCheckboxTypeOption(pb: CheckboxTypeOptionPB): CheckboxTypeOption {
return { return {
isSelected: pb.is_selected, isSelected: pb.dummy_field,
}; };
} }

View File

@ -9,7 +9,7 @@ export const CheckboxCell: FC<{
cell: CheckboxCellType; cell: CheckboxCellType;
}> = ({ field, cell }) => { }> = ({ field, cell }) => {
const viewId = useViewId(); const viewId = useViewId();
const checked = cell.data === 'Yes'; const checked = cell.data;
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
void cellService.updateCell(viewId, cell.rowId, field.id, !checked ? 'Yes' : 'No'); void cellService.updateCell(viewId, cell.rowId, field.id, !checked ? 'Yes' : 'No');

View File

@ -5,7 +5,7 @@ use bytes::Bytes;
use event_integration::event_builder::EventBuilder; use event_integration::event_builder::EventBuilder;
use event_integration::EventIntegrationTest; use event_integration::EventIntegrationTest;
use flowy_database2::entities::{ use flowy_database2::entities::{
CellChangesetPB, CellIdPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB, CellChangesetPB, CellIdPB, CheckboxCellDataPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB,
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, OrderObjectPositionPB, DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, OrderObjectPositionPB,
SelectOptionCellDataPB, UpdateRowMetaChangesetPB, SelectOptionCellDataPB, UpdateRowMetaChangesetPB,
}; };
@ -476,8 +476,8 @@ async fn update_checkbox_cell_event_test() {
assert!(error.is_none()); assert!(error.is_none());
let cell = test.get_cell(&grid_view.id, &row_id, &field_id).await; let cell = test.get_cell(&grid_view.id, &row_id, &field_id).await;
let output = String::from_utf8(cell.data).unwrap(); let output = CheckboxCellDataPB::try_from(Bytes::from(cell.data)).unwrap();
assert_eq!(output, "Yes"); assert!(output.is_checked);
} }
} }

View File

@ -1,24 +1,33 @@
use crate::services::field::CheckboxTypeOption; use crate::services::field::CheckboxTypeOption;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
#[derive(Default, Debug, Clone, ProtoBuf)]
pub struct CheckboxCellDataPB {
#[pb(index = 1)]
pub is_checked: bool,
}
impl CheckboxCellDataPB {
pub fn new(is_checked: bool) -> Self {
Self { is_checked }
}
}
#[derive(Debug, Clone, Default, ProtoBuf)] #[derive(Debug, Clone, Default, ProtoBuf)]
pub struct CheckboxTypeOptionPB { pub struct CheckboxTypeOptionPB {
/// unused
#[pb(index = 1)] #[pb(index = 1)]
pub is_selected: bool, pub dummy_field: bool,
} }
impl From<CheckboxTypeOption> for CheckboxTypeOptionPB { impl From<CheckboxTypeOption> for CheckboxTypeOptionPB {
fn from(data: CheckboxTypeOption) -> Self { fn from(_type_option: CheckboxTypeOption) -> Self {
Self { Self { dummy_field: false }
is_selected: data.is_selected,
}
} }
} }
impl From<CheckboxTypeOptionPB> for CheckboxTypeOption { impl From<CheckboxTypeOptionPB> for CheckboxTypeOption {
fn from(data: CheckboxTypeOptionPB) -> Self { fn from(_type_option: CheckboxTypeOptionPB) -> Self {
Self { Self()
is_selected: data.is_selected,
}
} }
} }

View File

@ -6,7 +6,7 @@ use collab_database::rows::{get_field_type_from_cell, Cell, Cells};
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use crate::entities::FieldType; use crate::entities::{CheckboxCellDataPB, FieldType};
use crate::services::cell::{CellCache, CellProtobufBlob}; use crate::services::cell::{CellCache, CellProtobufBlob};
use crate::services::field::checklist_type_option::ChecklistCellChangeset; use crate::services::field::checklist_type_option::ChecklistCellChangeset;
use crate::services::field::*; use crate::services::field::*;
@ -203,8 +203,8 @@ pub fn insert_url_cell(url: String, field: &Field) -> Cell {
apply_cell_changeset(url, None, field, None).unwrap() apply_cell_changeset(url, None, field, None).unwrap()
} }
pub fn insert_checkbox_cell(is_check: bool, field: &Field) -> Cell { pub fn insert_checkbox_cell(is_checked: bool, field: &Field) -> Cell {
let s = if is_check { let s = if is_checked {
CHECK.to_string() CHECK.to_string()
} else { } else {
UNCHECK.to_string() UNCHECK.to_string()
@ -343,8 +343,8 @@ impl<'a> CellBuilder<'a> {
} }
}, },
FieldType::Checkbox => { FieldType::Checkbox => {
if let Ok(value) = CheckboxCellData::from_cell_str(&cell_str) { if let Ok(value) = CheckboxCellDataPB::from_cell_str(&cell_str) {
cells.insert(field_id, insert_checkbox_cell(value.into_inner(), field)); cells.insert(field_id, insert_checkbox_cell(value.is_checked, field));
} }
}, },
FieldType::URL => { FieldType::URL => {
@ -399,13 +399,13 @@ impl<'a> CellBuilder<'a> {
} }
} }
pub fn insert_checkbox_cell(&mut self, field_id: &str, is_check: bool) { pub fn insert_checkbox_cell(&mut self, field_id: &str, is_checked: bool) {
match self.field_maps.get(&field_id.to_owned()) { match self.field_maps.get(&field_id.to_owned()) {
None => tracing::warn!("Can't find the checkbox field with id: {}", field_id), None => tracing::warn!("Can't find the checkbox field with id: {}", field_id),
Some(field) => { Some(field) => {
self self
.cells .cells
.insert(field_id.to_owned(), insert_checkbox_cell(is_check, field)); .insert(field_id.to_owned(), insert_checkbox_cell(is_checked, field));
}, },
} }
} }

View File

@ -1,20 +1,17 @@
use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB}; use crate::entities::{CheckboxCellDataPB, CheckboxFilterConditionPB, CheckboxFilterPB};
use crate::services::field::CheckboxCellData;
impl CheckboxFilterPB { impl CheckboxFilterPB {
pub fn is_visible(&self, cell_data: &CheckboxCellData) -> bool { pub fn is_visible(&self, cell_data: &CheckboxCellDataPB) -> bool {
let is_check = cell_data.is_check();
match self.condition { match self.condition {
CheckboxFilterConditionPB::IsChecked => is_check, CheckboxFilterConditionPB::IsChecked => cell_data.is_checked,
CheckboxFilterConditionPB::IsUnChecked => !is_check, CheckboxFilterConditionPB::IsUnChecked => !cell_data.is_checked,
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB}; use crate::entities::{CheckboxCellDataPB, CheckboxFilterConditionPB, CheckboxFilterPB};
use crate::services::field::CheckboxCellData;
use std::str::FromStr; use std::str::FromStr;
#[test] #[test]
@ -27,8 +24,9 @@ mod tests {
("yes", true), ("yes", true),
("false", false), ("false", false),
("no", false), ("no", false),
("", false),
] { ] {
let data = CheckboxCellData::from_str(value).unwrap(); let data = CheckboxCellDataPB::from_str(value).unwrap();
assert_eq!(checkbox_filter.is_visible(&data), visible); assert_eq!(checkbox_filter.is_visible(&data), visible);
} }
} }
@ -43,8 +41,9 @@ mod tests {
("no", true), ("no", true),
("true", false), ("true", false),
("yes", false), ("yes", false),
("", true),
] { ] {
let data = CheckboxCellData::from_str(value).unwrap(); let data = CheckboxCellDataPB::from_str(value).unwrap();
assert_eq!(checkbox_filter.is_visible(&data), visible); assert_eq!(checkbox_filter.is_visible(&data), visible);
} }
} }

View File

@ -2,6 +2,7 @@
mod tests { mod tests {
use collab_database::fields::Field; use collab_database::fields::Field;
use crate::entities::CheckboxCellDataPB;
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::CellDataDecoder; use crate::services::cell::CellDataDecoder;
use crate::services::cell::FromCellString; use crate::services::cell::FromCellString;
@ -27,9 +28,9 @@ mod tests {
assert_checkbox(&type_option, "NO", UNCHECK, &field_type, &field_rev); assert_checkbox(&type_option, "NO", UNCHECK, &field_type, &field_rev);
assert_checkbox(&type_option, "0", UNCHECK, &field_type, &field_rev); assert_checkbox(&type_option, "0", UNCHECK, &field_type, &field_rev);
// the checkout value will be empty if the value is letters or empty string // the checkout value will be uncheck as well if the value is letters or empty string
assert_checkbox(&type_option, "abc", "", &field_type, &field_rev); assert_checkbox(&type_option, "abc", UNCHECK, &field_type, &field_rev);
assert_checkbox(&type_option, "", "", &field_type, &field_rev); assert_checkbox(&type_option, "", UNCHECK, &field_type, &field_rev);
} }
fn assert_checkbox( fn assert_checkbox(
@ -42,7 +43,7 @@ mod tests {
assert_eq!( assert_eq!(
type_option type_option
.decode_cell( .decode_cell(
&CheckboxCellData::from_cell_str(input_str).unwrap().into(), &CheckboxCellDataPB::from_cell_str(input_str).unwrap().into(),
field_type, field_type,
field field
) )

View File

@ -1,30 +1,27 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::str::FromStr; use std::str::FromStr;
use collab::core::any_map::AnyMapExtension;
use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder}; use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder};
use collab_database::rows::Cell; use collab_database::rows::Cell;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use crate::entities::{CheckboxFilterPB, FieldType}; use crate::entities::{CheckboxCellDataPB, CheckboxFilterPB, FieldType};
use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::cell::{CellDataChangeset, CellDataDecoder};
use crate::services::field::{ use crate::services::field::{
CheckboxCellData, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde,
TypeOptionCellDataSerde, TypeOptionTransform, TypeOptionTransform,
}; };
use crate::services::sort::SortCondition; use crate::services::sort::SortCondition;
#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CheckboxTypeOption { pub struct CheckboxTypeOption();
pub is_selected: bool,
}
impl TypeOption for CheckboxTypeOption { impl TypeOption for CheckboxTypeOption {
type CellData = CheckboxCellData; type CellData = CheckboxCellDataPB;
type CellChangeset = CheckboxCellChangeset; type CellChangeset = CheckboxCellChangeset;
type CellProtobufType = CheckboxCellData; type CellProtobufType = CheckboxCellDataPB;
type CellFilter = CheckboxFilterPB; type CellFilter = CheckboxFilterPB;
} }
@ -47,7 +44,7 @@ impl TypeOptionTransform for CheckboxTypeOption {
_field: &Field, _field: &Field,
) -> Option<<Self as TypeOption>::CellData> { ) -> Option<<Self as TypeOption>::CellData> {
if transformed_field_type.is_text() { if transformed_field_type.is_text() {
Some(CheckboxCellData::from(cell)) Some(CheckboxCellDataPB::from(cell))
} else { } else {
None None
} }
@ -55,17 +52,14 @@ impl TypeOptionTransform for CheckboxTypeOption {
} }
impl From<TypeOptionData> for CheckboxTypeOption { impl From<TypeOptionData> for CheckboxTypeOption {
fn from(data: TypeOptionData) -> Self { fn from(_data: TypeOptionData) -> Self {
let is_selected = data.get_bool_value("is_selected").unwrap_or(false); Self()
CheckboxTypeOption { is_selected }
} }
} }
impl From<CheckboxTypeOption> for TypeOptionData { impl From<CheckboxTypeOption> for TypeOptionData {
fn from(data: CheckboxTypeOption) -> Self { fn from(_data: CheckboxTypeOption) -> Self {
TypeOptionDataBuilder::new() TypeOptionDataBuilder::new().build()
.insert_bool_value("is_selected", data.is_selected)
.build()
} }
} }
@ -78,7 +72,7 @@ impl TypeOptionCellDataSerde for CheckboxTypeOption {
} }
fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> { fn parse_cell(&self, cell: &Cell) -> FlowyResult<<Self as TypeOption>::CellData> {
Ok(CheckboxCellData::from(cell)) Ok(CheckboxCellDataPB::from(cell))
} }
} }
@ -105,7 +99,7 @@ impl CellDataDecoder for CheckboxTypeOption {
fn numeric_cell(&self, cell: &Cell) -> Option<f64> { fn numeric_cell(&self, cell: &Cell) -> Option<f64> {
let cell_data = self.parse_cell(cell).ok()?; let cell_data = self.parse_cell(cell).ok()?;
if cell_data.is_check() { if cell_data.is_checked {
Some(1.0) Some(1.0)
} else { } else {
Some(0.0) Some(0.0)
@ -121,7 +115,7 @@ impl CellDataChangeset for CheckboxTypeOption {
changeset: <Self as TypeOption>::CellChangeset, changeset: <Self as TypeOption>::CellChangeset,
_cell: Option<Cell>, _cell: Option<Cell>,
) -> FlowyResult<(Cell, <Self as TypeOption>::CellData)> { ) -> FlowyResult<(Cell, <Self as TypeOption>::CellData)> {
let checkbox_cell_data = CheckboxCellData::from_str(&changeset)?; let checkbox_cell_data = CheckboxCellDataPB::from_str(&changeset)?;
Ok((checkbox_cell_data.clone().into(), checkbox_cell_data)) Ok((checkbox_cell_data.clone().into(), checkbox_cell_data))
} }
} }
@ -147,7 +141,7 @@ impl TypeOptionCellDataCompare for CheckboxTypeOption {
other_cell_data: &<Self as TypeOption>::CellData, other_cell_data: &<Self as TypeOption>::CellData,
sort_condition: SortCondition, sort_condition: SortCondition,
) -> Ordering { ) -> Ordering {
let order = cell_data.is_check().cmp(&other_cell_data.is_check()); let order = cell_data.is_checked.cmp(&other_cell_data.is_checked);
sort_condition.evaluate_order(order) sort_condition.evaluate_order(order)
} }
@ -164,10 +158,10 @@ impl TypeOptionCellDataCompare for CheckboxTypeOption {
sort_condition: SortCondition, sort_condition: SortCondition,
) -> Ordering { ) -> Ordering {
match (cell_data, other_cell_data) { match (cell_data, other_cell_data) {
(None, Some(right_cell_data)) if right_cell_data.is_check() => { (None, Some(right_cell_data)) if right_cell_data.is_checked => {
sort_condition.evaluate_order(Ordering::Less) sort_condition.evaluate_order(Ordering::Less)
}, },
(Some(left_cell_data), None) if left_cell_data.is_check() => { (Some(left_cell_data), None) if left_cell_data.is_checked => {
sort_condition.evaluate_order(Ordering::Greater) sort_condition.evaluate_order(Ordering::Greater)
}, },
_ => Ordering::Equal, _ => Ordering::Equal,

View File

@ -3,93 +3,53 @@ use std::str::FromStr;
use bytes::Bytes; use bytes::Bytes;
use collab::core::any_map::AnyMapExtension; use collab::core::any_map::AnyMapExtension;
use collab_database::rows::{new_cell_builder, Cell}; use collab_database::rows::{new_cell_builder, Cell};
use protobuf::ProtobufError;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use crate::entities::FieldType; use crate::entities::{CheckboxCellDataPB, FieldType};
use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellString}; use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellString};
use crate::services::field::{TypeOptionCellData, CELL_DATA}; use crate::services::field::{TypeOptionCellData, CELL_DATA};
pub const CHECK: &str = "Yes"; pub const CHECK: &str = "Yes";
pub const UNCHECK: &str = "No"; pub const UNCHECK: &str = "No";
#[derive(Default, Debug, Clone)] impl TypeOptionCellData for CheckboxCellDataPB {
pub struct CheckboxCellData(pub String);
impl CheckboxCellData {
pub fn into_inner(self) -> bool {
self.is_check()
}
pub fn is_check(&self) -> bool {
self.0 == CHECK
}
pub fn is_uncheck(&self) -> bool {
self.0 == UNCHECK
}
}
impl TypeOptionCellData for CheckboxCellData {
fn is_cell_empty(&self) -> bool { fn is_cell_empty(&self) -> bool {
false false
} }
} }
impl AsRef<[u8]> for CheckboxCellData { impl From<&Cell> for CheckboxCellDataPB {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl From<&Cell> for CheckboxCellData {
fn from(cell: &Cell) -> Self { fn from(cell: &Cell) -> Self {
let value = cell.get_str_value(CELL_DATA).unwrap_or_default(); let value = cell.get_str_value(CELL_DATA).unwrap_or_default();
CheckboxCellData::from_cell_str(&value).unwrap_or_default() CheckboxCellDataPB::from_cell_str(&value).unwrap_or_default()
} }
} }
impl From<CheckboxCellData> for Cell { impl From<CheckboxCellDataPB> for Cell {
fn from(data: CheckboxCellData) -> Self { fn from(data: CheckboxCellDataPB) -> Self {
new_cell_builder(FieldType::Checkbox) new_cell_builder(FieldType::Checkbox)
.insert_str_value(CELL_DATA, data.to_string()) .insert_str_value(CELL_DATA, data.to_string())
.build() .build()
} }
} }
impl FromStr for CheckboxCellData { impl FromStr for CheckboxCellDataPB {
type Err = FlowyError; type Err = FlowyError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let lower_case_str: &str = &s.to_lowercase(); let lower_case_str: &str = &s.to_lowercase();
let val = match lower_case_str { let is_checked = match lower_case_str {
"1" => Some(true), "1" | "true" | "yes" => true,
"true" => Some(true), "0" | "false" | "no" => false,
"yes" => Some(true), _ => false,
"0" => Some(false),
"false" => Some(false),
"no" => Some(false),
_ => None,
}; };
match val { Ok(Self::new(is_checked))
Some(true) => Ok(Self(CHECK.to_string())),
Some(false) => Ok(Self(UNCHECK.to_string())),
None => Ok(Self("".to_string())),
}
} }
} }
impl std::convert::TryFrom<CheckboxCellData> for Bytes { impl FromCellString for CheckboxCellDataPB {
type Error = ProtobufError;
fn try_from(value: CheckboxCellData) -> Result<Self, Self::Error> {
Ok(Bytes::from(value.0))
}
}
impl FromCellString for CheckboxCellData {
fn from_cell_str(s: &str) -> FlowyResult<Self> fn from_cell_str(s: &str) -> FlowyResult<Self>
where where
Self: Sized, Self: Sized,
@ -98,27 +58,29 @@ impl FromCellString for CheckboxCellData {
} }
} }
impl ToString for CheckboxCellData { impl ToString for CheckboxCellDataPB {
fn to_string(&self) -> String { fn to_string(&self) -> String {
self.0.clone() if self.is_checked {
CHECK.to_string()
} else {
UNCHECK.to_string()
}
} }
} }
impl DecodedCellData for CheckboxCellData { impl DecodedCellData for CheckboxCellDataPB {
type Object = CheckboxCellData; type Object = CheckboxCellDataPB;
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.0.is_empty() false
} }
} }
pub struct CheckboxCellDataParser(); pub struct CheckboxCellDataParser();
impl CellProtobufBlobParser for CheckboxCellDataParser { impl CellProtobufBlobParser for CheckboxCellDataParser {
type Object = CheckboxCellData; type Object = CheckboxCellDataPB;
fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> { fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) { CheckboxCellDataPB::try_from(bytes.as_ref()).or_else(|_| Ok(CheckboxCellDataPB::default()))
Ok(s) => CheckboxCellData::from_cell_str(&s),
Err(_) => Ok(CheckboxCellData("".to_string())),
}
} }
} }

View File

@ -190,7 +190,7 @@ mod tests {
#[test] #[test]
fn multi_select_transform_with_checkbox_type_option_test() { fn multi_select_transform_with_checkbox_type_option_test() {
let checkbox_type_option = CheckboxTypeOption { is_selected: false }; let checkbox_type_option = CheckboxTypeOption();
let mut multi_select = MultiSelectTypeOption::default(); let mut multi_select = MultiSelectTypeOption::default();
multi_select.transform_type_option(FieldType::Checkbox, checkbox_type_option.clone().into()); multi_select.transform_type_option(FieldType::Checkbox, checkbox_type_option.clone().into());
debug_assert_eq!(multi_select.options.len(), 2); debug_assert_eq!(multi_select.options.len(), 2);

View File

@ -5,15 +5,15 @@ use serde::{Deserialize, Serialize};
use flowy_error::{internal_error, ErrorCode, FlowyResult}; use flowy_error::{internal_error, ErrorCode, FlowyResult};
use crate::entities::{FieldType, SelectOptionCellDataPB}; use crate::entities::{CheckboxCellDataPB, FieldType, SelectOptionCellDataPB};
use crate::services::cell::{ use crate::services::cell::{
CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, ToCellChangeset, CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, ToCellChangeset,
}; };
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper; use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
use crate::services::field::{ use crate::services::field::{
make_selected_options, CheckboxCellData, MultiSelectTypeOption, SelectOption, make_selected_options, MultiSelectTypeOption, SelectOption, SelectOptionCellData,
SelectOptionCellData, SelectOptionColor, SelectOptionIds, SingleSelectTypeOption, TypeOption, SelectOptionColor, SelectOptionIds, SingleSelectTypeOption, TypeOption, TypeOptionCellDataSerde,
TypeOptionCellDataSerde, TypeOptionTransform, SELECTION_IDS_SEPARATOR, TypeOptionTransform, SELECTION_IDS_SEPARATOR,
}; };
/// Defines the shared actions used by SingleSelect or Multi-Select. /// Defines the shared actions used by SingleSelect or Multi-Select.
@ -104,7 +104,7 @@ where
None None
}, },
FieldType::Checkbox => { FieldType::Checkbox => {
let cell_content = CheckboxCellData::from(cell).to_string(); let cell_content = CheckboxCellDataPB::from(cell).to_string();
let mut transformed_ids = Vec::new(); let mut transformed_ids = Vec::new();
let options = self.options(); let options = self.options();
if let Some(option) = options.iter().find(|option| option.name == cell_content) { if let Some(option) = options.iter().find(|option| option.name == cell_content) {

View File

@ -47,7 +47,7 @@ impl GroupCustomize for CheckboxGroupController {
content: &str, content: &str,
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData, cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> bool { ) -> bool {
if cell_data.is_check() { if cell_data.is_checked {
content == CHECK content == CHECK
} else { } else {
content == UNCHECK content == UNCHECK
@ -64,7 +64,7 @@ impl GroupCustomize for CheckboxGroupController {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
let is_not_contained = !group.contains_row(&row_detail.row.id); let is_not_contained = !group.contains_row(&row_detail.row.id);
if group.id == CHECK { if group.id == CHECK {
if cell_data.is_uncheck() { if !cell_data.is_checked {
// Remove the row if the group.id is CHECK but the cell_data is UNCHECK // Remove the row if the group.id is CHECK but the cell_data is UNCHECK
changeset changeset
.deleted_rows .deleted_rows
@ -82,7 +82,7 @@ impl GroupCustomize for CheckboxGroupController {
} }
if group.id == UNCHECK { if group.id == UNCHECK {
if cell_data.is_check() { if cell_data.is_checked {
// Remove the row if the group.id is UNCHECK but the cell_data is CHECK // Remove the row if the group.id is UNCHECK but the cell_data is CHECK
changeset changeset
.deleted_rows .deleted_rows
@ -154,8 +154,8 @@ impl GroupController for CheckboxGroupController {
match self.context.get_group(group_id) { match self.context.get_group(group_id) {
None => tracing::warn!("Can not find the group: {}", group_id), None => tracing::warn!("Can not find the group: {}", group_id),
Some((_, group)) => { Some((_, group)) => {
let is_check = group.id == CHECK; let is_checked = group.id == CHECK;
let cell = insert_checkbox_cell(is_check, field); let cell = insert_checkbox_cell(is_checked, field);
cells.insert(field.id.clone(), cell); cells.insert(field.id.clone(), cell);
}, },
} }