mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
commit
82182d7872
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>,
|
||||
|
@ -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>> {
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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(¶ms.field_id, cell_bytes.to_vec()))
|
||||
let (field_type, cell_bytes) = self.decode_any_cell_data(params).await?;
|
||||
Some(GridCellPB::new(¶ms.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(¶ms.field_id).await?;
|
||||
let row_rev = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??;
|
||||
|
||||
let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone();
|
||||
Some(decode_any_cell_data(cell_rev.data, &field_rev))
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user