Merge pull request #1182 from AppFlowy-IO/fix/switch_to_text_property

fix: display cell data after switching to text field
This commit is contained in:
Nathan.fooo 2022-09-27 19:05:10 +08:00 committed by GitHub
commit 82182d7872
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 193 additions and 39 deletions

View File

@ -97,7 +97,6 @@ class GridURLCell extends GridCellWidget {
class _GridURLCellState extends GridCellState<GridURLCell> {
final _popoverController = PopoverController();
GridURLCellController? _cellContext;
late URLCellBloc _cellBloc;
@override
@ -132,6 +131,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(300, 160)),
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerFlags.none,
offset: const Offset(0, 20),
child: SizedBox.expand(
child: GestureDetector(
@ -144,7 +144,8 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
),
popupBuilder: (BuildContext popoverContext) {
return URLEditorPopover(
cellController: _cellContext!,
cellController: widget.cellControllerBuilder.build()
as GridURLCellController,
);
},
onClose: () {
@ -166,17 +167,13 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
final uri = Uri.parse(url);
if (url.isNotEmpty && await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
_cellContext =
widget.cellControllerBuilder.build() as GridURLCellController;
widget.onCellEditing.value = true;
_popoverController.show();
}
}
@override
void requestBeginFocus() {
_openUrlOrEdit(_cellBloc.state.url);
widget.onCellEditing.value = true;
_popoverController.show();
}
@override

View File

@ -1,3 +1,4 @@
use crate::entities::FieldType;
use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr;
@ -74,15 +75,20 @@ pub struct GridCellPB {
#[pb(index = 1)]
pub field_id: String,
// The data was encoded in field_type's data type
#[pb(index = 2)]
pub data: Vec<u8>,
#[pb(index = 3, one_of)]
pub field_type: Option<FieldType>,
}
impl GridCellPB {
pub fn new(field_id: &str, data: Vec<u8>) -> Self {
pub fn new(field_id: &str, field_type: FieldType, data: Vec<u8>) -> Self {
Self {
field_id: field_id.to_owned(),
data,
field_type: Some(field_type),
}
}
@ -90,6 +96,7 @@ impl GridCellPB {
Self {
field_id: field_id.to_owned(),
data: vec![],
field_type: None,
}
}
}

View File

@ -24,6 +24,13 @@ pub trait CellDisplayable<CD> {
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<CellBytes>;
fn display_string(
&self,
cell_data: CellData<CD>,
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<String>;
}
// CD: Short for CellData. This type is the type return by apply_changeset function.
@ -84,16 +91,16 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
pub fn decode_any_cell_data<T: TryInto<AnyCellData, Error = FlowyError> + Debug>(
data: T,
field_rev: &FieldRevision,
) -> CellBytes {
) -> (FieldType, CellBytes) {
let to_field_type = field_rev.ty.into();
match data.try_into() {
Ok(any_cell_data) => {
let AnyCellData { data, field_type } = any_cell_data;
let to_field_type = field_rev.ty.into();
match try_decode_cell_data(data.into(), &field_type, &to_field_type, field_rev) {
Ok(cell_bytes) => cell_bytes,
Ok(cell_bytes) => (field_type, cell_bytes),
Err(e) => {
tracing::error!("Decode cell data failed, {:?}", e);
CellBytes::default()
(field_type, CellBytes::default())
}
}
}
@ -101,12 +108,58 @@ pub fn decode_any_cell_data<T: TryInto<AnyCellData, Error = FlowyError> + Debug>
// It's okay to ignore this error, because it's okay that the current cell can't
// display the existing cell data. For example, the UI of the text cell will be blank if
// the type of the data of cell is Number.
CellBytes::default()
(to_field_type, CellBytes::default())
}
}
}
/// Use the `to_field_type`'s TypeOption to parse the cell data into `from_field_type`'s data.
pub fn decode_cell_data_to_string(
cell_data: CellData<String>,
from_field_type: &FieldType,
to_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data = cell_data.try_into_inner()?;
let get_cell_display_str = || {
let field_type: FieldTypeRevision = to_field_type.into();
let result = match to_field_type {
FieldType::RichText => field_rev
.get_type_option::<RichTextTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::Number => field_rev
.get_type_option::<NumberTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::DateTime => field_rev
.get_type_option::<DateTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::SingleSelect => field_rev
.get_type_option::<SingleSelectTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::MultiSelect => field_rev
.get_type_option::<MultiSelectTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::Checkbox => field_rev
.get_type_option::<CheckboxTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::URL => field_rev
.get_type_option::<URLTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
};
Some(result)
};
match get_cell_display_str() {
Some(Ok(s)) => Ok(s),
Some(Err(err)) => {
tracing::error!("{:?}", err);
Ok("".to_owned())
}
None => Ok("".to_owned()),
}
}
/// Use the `to_field_type`'s TypeOption to parse the cell data into `from_field_type` type's data.
///
/// Each `FieldType` has its corresponding `TypeOption` that implements the `CellDisplayable`
/// and `CellDataOperation` traits.

View File

@ -48,6 +48,16 @@ impl CellDisplayable<CheckboxCellData> for CheckboxTypeOptionPB {
let cell_data = cell_data.try_into_inner()?;
Ok(CellBytes::new(cell_data))
}
fn display_string(
&self,
cell_data: CellData<CheckboxCellData>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data = cell_data.try_into_inner()?;
Ok(cell_data.to_string())
}
}
impl CellDataOperation<CheckboxCellData, String> for CheckboxTypeOptionPB {

View File

@ -127,6 +127,17 @@ impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
let date_cell_data = self.today_desc_from_timestamp(timestamp);
CellBytes::from(date_cell_data)
}
fn display_string(
&self,
cell_data: CellData<DateTimestamp>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let timestamp = cell_data.try_into_inner()?;
let date_cell_data = self.today_desc_from_timestamp(timestamp);
Ok(date_cell_data.date)
}
}
impl CellDataOperation<DateTimestamp, DateCellChangesetPB> for DateTypeOptionPB {

View File

@ -1,6 +1,6 @@
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation};
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
use crate::services::field::type_options::number_type_option::format::*;
use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
use bytes::Bytes;
@ -102,17 +102,13 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
s
}
impl CellDataOperation<String, String> for NumberTypeOptionPB {
fn decode_cell_data(
impl CellDisplayable<String> for NumberTypeOptionPB {
fn display_data(
&self,
cell_data: CellData<String>,
decoded_field_type: &FieldType,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<CellBytes> {
if decoded_field_type.is_date() {
return Ok(CellBytes::default());
}
let cell_data: String = cell_data.try_into_inner()?;
match self.format_cell_data(&cell_data) {
Ok(num) => Ok(CellBytes::new(num.to_string())),
@ -120,6 +116,31 @@ impl CellDataOperation<String, String> for NumberTypeOptionPB {
}
}
fn display_string(
&self,
cell_data: CellData<String>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data: String = cell_data.try_into_inner()?;
Ok(cell_data)
}
}
impl CellDataOperation<String, String> for NumberTypeOptionPB {
fn decode_cell_data(
&self,
cell_data: CellData<String>,
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<CellBytes> {
if decoded_field_type.is_date() {
return Ok(CellBytes::default());
}
self.display_data(cell_data, decoded_field_type, field_rev)
}
fn apply_changeset(
&self,
changeset: CellDataChangeset<String>,

View File

@ -120,6 +120,21 @@ where
) -> FlowyResult<CellBytes> {
CellBytes::from(self.selected_select_option(cell_data))
}
fn display_string(
&self,
cell_data: CellData<SelectOptionIds>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
Ok(self
.selected_select_option(cell_data)
.select_options
.into_iter()
.map(|option| option.name)
.collect::<Vec<String>>()
.join(SELECTION_IDS_SEPARATOR))
}
}
pub fn select_option_operation(field_rev: &FieldRevision) -> FlowyResult<Box<dyn SelectOptionOperation>> {

View File

@ -17,10 +17,10 @@ mod tests {
type_option
.decode_cell_data(1647251762.into(), &field_type, &field_rev)
.unwrap()
.parser::<DateCellDataParser>()
.parser::<TextCellDataParser>()
.unwrap()
.date,
"Mar 14,2022".to_owned()
.as_ref(),
"Mar 14,2022"
);
}
@ -40,10 +40,10 @@ mod tests {
type_option
.decode_cell_data(option_id.into(), &field_type, &field_rev)
.unwrap()
.parser::<SelectOptionCellDataParser>()
.parser::<TextCellDataParser>()
.unwrap()
.select_options,
vec![done_option],
.to_string(),
done_option.name,
);
}
}

View File

@ -1,8 +1,8 @@
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{
try_decode_cell_data, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation, CellDisplayable,
FromCellString,
decode_cell_data_to_string, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation,
CellDisplayable, FromCellString,
};
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use bytes::Bytes;
@ -44,6 +44,16 @@ impl CellDisplayable<String> for RichTextTypeOptionPB {
let cell_str: String = cell_data.try_into_inner()?;
Ok(CellBytes::new(cell_str))
}
fn display_string(
&self,
cell_data: CellData<String>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_str: String = cell_data.try_into_inner()?;
Ok(cell_str)
}
}
impl CellDataOperation<String, String> for RichTextTypeOptionPB {
@ -57,8 +67,10 @@ impl CellDataOperation<String, String> for RichTextTypeOptionPB {
|| decoded_field_type.is_single_select()
|| decoded_field_type.is_multi_select()
|| decoded_field_type.is_number()
|| decoded_field_type.is_url()
{
try_decode_cell_data(cell_data, decoded_field_type, decoded_field_type, field_rev)
let s = decode_cell_data_to_string(cell_data, decoded_field_type, decoded_field_type, field_rev);
Ok(CellBytes::new(s.unwrap_or_else(|_| "".to_owned())))
} else {
self.display_data(cell_data, decoded_field_type, field_rev)
}
@ -85,6 +97,14 @@ impl AsRef<str> for TextCellData {
}
}
impl std::ops::Deref for TextCellData {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl FromCellString for TextCellData {
fn from_cell_str(s: &str) -> FlowyResult<Self>
where
@ -94,6 +114,12 @@ impl FromCellString for TextCellData {
}
}
impl ToString for TextCellData {
fn to_string(&self) -> String {
self.0.clone()
}
}
pub struct TextCellDataParser();
impl CellBytesParser for TextCellDataParser {
type Object = TextCellData;

View File

@ -42,6 +42,16 @@ impl CellDisplayable<URLCellDataPB> for URLTypeOptionPB {
let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
CellBytes::from(cell_data)
}
fn display_string(
&self,
cell_data: CellData<URLCellDataPB>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
Ok(cell_data.content)
}
}
impl CellDataOperation<URLCellDataPB, String> for URLTypeOptionPB {

View File

@ -435,14 +435,18 @@ impl GridRevisionEditor {
}
pub async fn get_cell(&self, params: &GridCellIdParams) -> Option<GridCellPB> {
let cell_bytes = self.get_cell_bytes(params).await?;
Some(GridCellPB::new(&params.field_id, cell_bytes.to_vec()))
let (field_type, cell_bytes) = self.decode_any_cell_data(params).await?;
Some(GridCellPB::new(&params.field_id, field_type, cell_bytes.to_vec()))
}
pub async fn get_cell_bytes(&self, params: &GridCellIdParams) -> Option<CellBytes> {
let (_, cell_data) = self.decode_any_cell_data(params).await?;
Some(cell_data)
}
async fn decode_any_cell_data(&self, params: &GridCellIdParams) -> Option<(FieldType, CellBytes)> {
let field_rev = self.get_field_rev(&params.field_id).await?;
let row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
Some(decode_any_cell_data(cell_rev.data, &field_rev))
}

View File

@ -205,7 +205,7 @@ where
if let Some(cell_rev) = cell_rev {
let mut grouped_rows: Vec<GroupedRow> = vec![];
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
for group in self.group_ctx.groups() {
if self.can_group(&group.filter_content, &cell_data) {
@ -244,7 +244,7 @@ where
field_rev: &FieldRevision,
) -> FlowyResult<Vec<GroupChangesetPB>> {
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
let mut changesets = self.add_row_if_match(row_rev, &cell_data);
let default_group_changeset = self.update_default_group(row_rev, &changesets);
@ -265,7 +265,7 @@ where
) -> FlowyResult<Vec<GroupChangesetPB>> {
// if the cell_rev is none, then the row must be crated from the default group.
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
Ok(self.remove_row_if_match(row_rev, &cell_data))
} else {
@ -285,7 +285,7 @@ where
};
if let Some(cell_rev) = cell_rev {
let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
Ok(self.move_row(&cell_data, context))
} else {