mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: checkbox field type (#4682)
This commit is contained in:
parent
a6c6aa819c
commit
334aedd6c6
@ -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";
|
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
@ -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) {
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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');
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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,
|
||||||
|
@ -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())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user