Browse Source

refactor: cell with origin data

appflowy 3 years ago
parent
commit
f617a04900

+ 31 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart

@@ -1,3 +1,4 @@
+import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field;
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -6,6 +7,7 @@ import 'package:table_calendar/table_calendar.dart';
 import 'dart:async';
 import 'cell_service.dart';
 import 'package:dartz/dartz.dart';
+import 'package:fixnum/fixnum.dart' as $fixnum;
 part 'date_cal_bloc.freezed.dart';
 
 class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
@@ -15,9 +17,10 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
   DateCalBloc({required this.cellContext}) : super(DateCalState.initial(cellContext)) {
     on<DateCalEvent>(
       (event, emit) async {
-        event.map(
-          initial: (_Initial value) {
+        await event.map(
+          initial: (_Initial value) async {
             _startListening();
+            await _loadDateTypeOption(emit);
           },
           selectDay: (_SelectDay value) {
             if (!isSameDay(state.selectedDay, value.day)) {
@@ -60,6 +63,30 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
     );
   }
 
+  Future<void> _loadDateTypeOption(Emitter<DateCalState> emit) async {
+    final result = await cellContext.getTypeOptionData();
+    result.fold(
+      (data) {
+        final typeOptionData = DateTypeOption.fromBuffer(data);
+
+        DateTime? selectedDay;
+        final cellData = cellContext.getCellData()?.data;
+
+        if (cellData != null) {
+          final timestamp = $fixnum.Int64.parseInt(cellData).toInt();
+          selectedDay = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
+        }
+
+        emit(state.copyWith(
+          typeOptinoData: some(typeOptionData),
+          includeTime: typeOptionData.includeTime,
+          selectedDay: selectedDay,
+        ));
+      },
+      (err) => Log.error(err),
+    );
+  }
+
   void _updateCellData(DateTime day) {
     final data = day.millisecondsSinceEpoch ~/ 1000;
     cellContext.saveCellData(data.toString());
@@ -83,6 +110,7 @@ class DateCalState with _$DateCalState {
     required Option<DateTypeOption> typeOptinoData,
     required CalendarFormat format,
     required DateTime focusedDay,
+    required bool includeTime,
     DateTime? selectedDay,
   }) = _DateCalState;
 
@@ -91,5 +119,6 @@ class DateCalState with _$DateCalState {
         typeOptinoData: none(),
         format: CalendarFormat.month,
         focusedDay: DateTime.now(),
+        includeTime: false,
       );
 }

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -119,6 +119,7 @@ class FieldService {
     required FieldType fieldType,
   }) {
     final payload = EditFieldPayload.create()
+      ..gridId = gridId
       ..fieldId = fieldId
       ..fieldType = fieldType;
     return GridEventGetFieldTypeOption(payload).send().then((result) {

+ 14 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart

@@ -1350,6 +1350,7 @@ class Cell extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Cell', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
     ..hasRequiredFields = false
   ;
 
@@ -1357,6 +1358,7 @@ class Cell extends $pb.GeneratedMessage {
   factory Cell({
     $core.String? fieldId,
     $core.String? content,
+    $core.String? data,
   }) {
     final _result = create();
     if (fieldId != null) {
@@ -1365,6 +1367,9 @@ class Cell extends $pb.GeneratedMessage {
     if (content != null) {
       _result.content = content;
     }
+    if (data != null) {
+      _result.data = data;
+    }
     return _result;
   }
   factory Cell.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -1405,6 +1410,15 @@ class Cell extends $pb.GeneratedMessage {
   $core.bool hasContent() => $_has(1);
   @$pb.TagNumber(2)
   void clearContent() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get data => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set data($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasData() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearData() => clearField(3);
 }
 
 class RepeatedCell extends $pb.GeneratedMessage {

+ 2 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart

@@ -291,11 +291,12 @@ const Cell$json = const {
   '2': const [
     const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
     const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'},
+    const {'1': 'data', '3': 3, '4': 1, '5': 9, '10': 'data'},
   ],
 };
 
 /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQ=');
+final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSEgoEZGF0YRgDIAEoCVIEZGF0YQ==');
 @$core.Deprecated('Use repeatedCellDescriptor instead')
 const RepeatedCell$json = const {
   '1': 'RepeatedCell',

+ 1 - 1
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -245,7 +245,7 @@ pub(crate) async fn get_cell_handler(
     let params: CellIdentifier = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     match editor.get_cell(&params).await {
-        None => data_result(Cell::new(&params.field_id, "".to_owned())),
+        None => data_result(Cell::empty(&params.field_id)),
         Some(cell) => data_result(cell),
     }
 }

+ 5 - 5
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs

@@ -1,6 +1,6 @@
 use crate::impl_type_option;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellData};
+use crate::services::row::{CellDataChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::FlowyError;
@@ -44,18 +44,18 @@ const YES: &str = "Yes";
 const NO: &str = "No";
 
 impl CellDataOperation for CheckboxTypeOption {
-    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
+    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
             if !type_option_cell_data.is_checkbox() {
-                return String::new();
+                return DecodedCellData::default();
             }
             let cell_data = type_option_cell_data.data;
             if cell_data == YES || cell_data == NO {
-                return cell_data;
+                return DecodedCellData::from_content(cell_data);
             }
         }
 
-        String::new()
+        DecodedCellData::default()
     }
 
     fn apply_changeset<T: Into<CellDataChangeset>>(

+ 10 - 9
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -1,8 +1,8 @@
 use crate::impl_type_option;
-use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellData};
+use crate::services::row::{CellDataChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
 use bytes::Bytes;
 use chrono::format::strftime::StrftimeItems;
-use chrono::NaiveDateTime;
+use chrono::{NaiveDateTime, ParseResult};
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::FlowyError;
 use flowy_grid_data_model::entities::{
@@ -53,24 +53,25 @@ impl DateTypeOption {
 }
 
 impl CellDataOperation for DateTypeOption {
-    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
+    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
             if !type_option_cell_data.is_date() {
-                return String::new();
+                return DecodedCellData::default();
             }
 
             let cell_data = type_option_cell_data.data;
             if let Ok(timestamp) = cell_data.parse::<i64>() {
                 let native = NaiveDateTime::from_timestamp(timestamp, 0);
-                return self.today_from_native(native);
+                return DecodedCellData::new(format!("{}", timestamp), self.today_from_native(native));
             }
 
-            if NaiveDateTime::parse_from_str(&cell_data, &self.fmt_str()).is_ok() {
-                return cell_data;
-            }
+            return match NaiveDateTime::parse_from_str(&cell_data, &self.fmt_str()) {
+                Ok(date_time) => DecodedCellData::new(format!("{}", date_time.timestamp()), cell_data),
+                Err(_) => DecodedCellData::default(),
+            };
         }
 
-        String::new()
+        DecodedCellData::default()
     }
 
     fn apply_changeset<T: Into<CellDataChangeset>>(

+ 17 - 11
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs

@@ -1,6 +1,6 @@
 use crate::impl_type_option;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellData};
+use crate::services::row::{CellDataChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::FlowyError;
@@ -77,30 +77,36 @@ pub struct NumberTypeOption {
 impl_type_option!(NumberTypeOption, FieldType::Number);
 
 impl CellDataOperation for NumberTypeOption {
-    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
+    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
             if type_option_cell_data.is_date() {
-                return String::new();
+                return DecodedCellData::default();
             }
 
             let cell_data = type_option_cell_data.data;
-            match self.format {
+            return match self.format {
                 NumberFormat::Number => {
                     if let Ok(v) = cell_data.parse::<f64>() {
-                        return v.to_string();
+                        return DecodedCellData::from_content(v.to_string());
                     }
 
                     if let Ok(v) = cell_data.parse::<i64>() {
-                        return v.to_string();
+                        return DecodedCellData::from_content(v.to_string());
                     }
 
-                    return String::new();
+                    DecodedCellData::default()
                 }
-                NumberFormat::Percent => cell_data.parse::<f64>().map_or(String::new(), |v| v.to_string()),
-                _ => self.money_from_str(&cell_data),
-            }
+                NumberFormat::Percent => {
+                    let content = cell_data.parse::<f64>().map_or(String::new(), |v| v.to_string());
+                    DecodedCellData::from_content(content)
+                }
+                _ => {
+                    let content = self.money_from_str(&cell_data);
+                    DecodedCellData::from_content(content)
+                }
+            };
         } else {
-            String::new()
+            DecodedCellData::default()
         }
     }
 

+ 17 - 16
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

@@ -1,7 +1,7 @@
 use crate::impl_type_option;
 use crate::services::entities::{CellIdentifier, CellIdentifierPayload};
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellData};
+use crate::services::row::{CellDataChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
@@ -95,22 +95,21 @@ impl SelectOptionOperation for SingleSelectTypeOption {
 }
 
 impl CellDataOperation for SingleSelectTypeOption {
-    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
+    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
             if !type_option_cell_data.is_single_select() {
-                return String::new();
+                return DecodedCellData::default();
             }
 
-            match select_option_ids(type_option_cell_data.data).first() {
-                None => String::new(),
-                Some(option_id) => match self.options.iter().find(|option| &option.id == option_id) {
-                    None => String::new(),
-                    Some(option) => option.name.clone(),
-                },
+            if let Some(option_id) = select_option_ids(type_option_cell_data.data).first() {
+                return match self.options.iter().find(|option| &option.id == option_id) {
+                    None => DecodedCellData::default(),
+                    Some(option) => DecodedCellData::from_content(option.name.clone()),
+                };
             }
-        } else {
-            String::new()
         }
+
+        DecodedCellData::default()
     }
 
     fn apply_changeset<T: Into<CellDataChangeset>>(
@@ -194,20 +193,22 @@ impl SelectOptionOperation for MultiSelectTypeOption {
 }
 
 impl CellDataOperation for MultiSelectTypeOption {
-    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
+    fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
             if !type_option_cell_data.is_multi_select() {
-                return String::new();
+                return DecodedCellData::default();
             }
             let option_ids = select_option_ids(type_option_cell_data.data);
-            self.options
+            let content = self
+                .options
                 .iter()
                 .filter(|option| option_ids.contains(&option.id))
                 .map(|option| option.name.clone())
                 .collect::<Vec<String>>()
-                .join(SELECTION_IDS_SEPARATOR)
+                .join(SELECTION_IDS_SEPARATOR);
+            DecodedCellData::from_content(content)
         } else {
-            String::new()
+            DecodedCellData::default()
         }
     }
 

+ 8 - 5
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs

@@ -1,6 +1,8 @@
 use crate::impl_type_option;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{decode_cell_data, CellDataChangeset, CellDataOperation, TypeOptionCellData};
+use crate::services::row::{
+    decode_cell_data, CellDataChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData,
+};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::FlowyError;
@@ -33,19 +35,20 @@ pub struct RichTextTypeOption {
 impl_type_option!(RichTextTypeOption, FieldType::RichText);
 
 impl CellDataOperation for RichTextTypeOption {
-    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> String {
+    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
             if type_option_cell_data.is_date()
                 || type_option_cell_data.is_single_select()
                 || type_option_cell_data.is_multi_select()
                 || type_option_cell_data.is_number()
             {
-                decode_cell_data(data, field_meta, &type_option_cell_data.field_type).unwrap_or_else(|| "".to_owned())
+                decode_cell_data(data, field_meta, &type_option_cell_data.field_type)
+                    .unwrap_or_else(|| DecodedCellData::default())
             } else {
-                type_option_cell_data.data
+                DecodedCellData::from_content(type_option_cell_data.data)
             }
         } else {
-            String::new()
+            DecodedCellData::default()
         }
     }
 

+ 32 - 18
frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs

@@ -1,12 +1,14 @@
 use crate::services::field::*;
+use std::borrow::Cow;
 use std::fmt::Formatter;
+use std::sync::Arc;
 
 use flowy_error::FlowyError;
 use flowy_grid_data_model::entities::{CellMeta, FieldMeta, FieldType};
 use serde::{Deserialize, Serialize};
 
 pub trait CellDataOperation {
-    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> String;
+    fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData;
     fn apply_changeset<T: Into<CellDataChangeset>>(
         &self,
         changeset: T,
@@ -106,22 +108,31 @@ pub fn apply_cell_data_changeset<T: Into<CellDataChangeset>>(
         FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta),
     }
 }
-//
-// #[tracing::instrument(level = "trace", skip(field_meta, data), fields(content), err)]
-// pub fn decode_cell_data(data: String, field_meta: &FieldMeta, field_type: &FieldType) -> Result<String, FlowyError> {
-//     let s = match field_meta.field_type {
-//         FieldType::RichText => RichTextTypeOption::from(field_meta).decode_cell_data(data, field_meta),
-//         FieldType::Number => NumberTypeOption::from(field_meta).decode_cell_data(data, field_meta),
-//         FieldType::DateTime => DateTypeOption::from(field_meta).decode_cell_data(data, field_meta),
-//         FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).decode_cell_data(data, field_meta),
-//         FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).decode_cell_data(data, field_meta),
-//         FieldType::Checkbox => CheckboxTypeOption::from(field_meta).decode_cell_data(data, field_meta),
-//     };
-//     tracing::Span::current().record("content", &format!("{:?}: {}", field_meta.field_type, s).as_str());
-//     Ok(s)
-// }
-
-pub fn decode_cell_data(data: String, field_meta: &FieldMeta, field_type: &FieldType) -> Option<String> {
+
+#[derive(Default)]
+pub struct DecodedCellData {
+    raw: String,
+    content: String,
+}
+
+impl DecodedCellData {
+    pub fn from_content(content: String) -> Self {
+        Self {
+            raw: content.clone(),
+            content,
+        }
+    }
+
+    pub fn new(raw: String, content: String) -> Self {
+        Self { raw, content }
+    }
+
+    pub fn split(self) -> (String, String) {
+        (self.raw, self.content)
+    }
+}
+
+pub fn decode_cell_data(data: String, field_meta: &FieldMeta, field_type: &FieldType) -> Option<DecodedCellData> {
     let s = match field_type {
         FieldType::RichText => field_meta
             .get_type_option_entry::<RichTextTypeOption>(field_type)?
@@ -142,6 +153,9 @@ pub fn decode_cell_data(data: String, field_meta: &FieldMeta, field_type: &Field
             .get_type_option_entry::<CheckboxTypeOption>(field_type)?
             .decode_cell_data(data, field_meta),
     };
-    tracing::Span::current().record("content", &format!("{:?}: {}", field_meta.field_type, s).as_str());
+    tracing::Span::current().record(
+        "content",
+        &format!("{:?}: {}", field_meta.field_type, s.content).as_str(),
+    );
     Some(s)
 }

+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs

@@ -31,15 +31,15 @@ pub fn make_cell_by_field_id(
     cell_meta: CellMeta,
 ) -> Option<(String, Cell)> {
     let field_meta = field_map.get(&field_id)?;
-    let content = decode_cell_data(cell_meta.data, field_meta, &field_meta.field_type)?;
-    let cell = Cell::new(&field_id, content);
+    let (raw, content) = decode_cell_data(cell_meta.data.clone(), field_meta, &field_meta.field_type)?.split();
+    let cell = Cell::new(&field_id, content, raw);
     Some((field_id, cell))
 }
 
 pub fn make_cell(field_id: &str, field_meta: &FieldMeta, row_meta: &RowMeta) -> Option<Cell> {
     let cell_meta = row_meta.cells.get(field_id)?.clone();
-    let content = decode_cell_data(cell_meta.data, field_meta, &field_meta.field_type)?;
-    Some(Cell::new(field_id, content))
+    let (raw, content) = decode_cell_data(cell_meta.data.clone(), field_meta, &field_meta.field_type)?.split();
+    Some(Cell::new(field_id, content, raw))
 }
 
 pub(crate) fn make_row_orders_from_row_metas(row_metas: &[Arc<RowMeta>]) -> Vec<RowOrder> {

+ 4 - 1
frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs

@@ -287,7 +287,10 @@ async fn grid_row_add_date_cell_test() {
     let date_field = date_field.unwrap();
     let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
     assert_eq!(
-        decode_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type).unwrap(),
+        decode_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type)
+            .unwrap()
+            .split()
+            .1,
         "2022/03/16",
     );
     let scripts = vec![CreateRow { context }];

+ 13 - 1
shared-lib/flowy-grid-data-model/src/entities/grid.rs

@@ -461,13 +461,25 @@ pub struct Cell {
 
     #[pb(index = 2)]
     pub content: String,
+
+    #[pb(index = 3)]
+    pub data: String,
 }
 
 impl Cell {
-    pub fn new(field_id: &str, content: String) -> Self {
+    pub fn new(field_id: &str, content: String, data: String) -> Self {
         Self {
             field_id: field_id.to_owned(),
             content,
+            data,
+        }
+    }
+
+    pub fn empty(field_id: &str) -> Self {
+        Self {
+            field_id: field_id.to_owned(),
+            content: "".to_string(),
+            data: "".to_string(),
         }
     }
 }

+ 81 - 39
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -4692,6 +4692,7 @@ pub struct Cell {
     // message fields
     pub field_id: ::std::string::String,
     pub content: ::std::string::String,
+    pub data: ::std::string::String,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -4759,6 +4760,32 @@ impl Cell {
     pub fn take_content(&mut self) -> ::std::string::String {
         ::std::mem::replace(&mut self.content, ::std::string::String::new())
     }
+
+    // string data = 3;
+
+
+    pub fn get_data(&self) -> &str {
+        &self.data
+    }
+    pub fn clear_data(&mut self) {
+        self.data.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_data(&mut self, v: ::std::string::String) {
+        self.data = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_data(&mut self) -> &mut ::std::string::String {
+        &mut self.data
+    }
+
+    // Take field
+    pub fn take_data(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.data, ::std::string::String::new())
+    }
 }
 
 impl ::protobuf::Message for Cell {
@@ -4776,6 +4803,9 @@ impl ::protobuf::Message for Cell {
                 2 => {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?;
                 },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?;
+                },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
                 },
@@ -4794,6 +4824,9 @@ impl ::protobuf::Message for Cell {
         if !self.content.is_empty() {
             my_size += ::protobuf::rt::string_size(2, &self.content);
         }
+        if !self.data.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.data);
+        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -4806,6 +4839,9 @@ impl ::protobuf::Message for Cell {
         if !self.content.is_empty() {
             os.write_string(2, &self.content)?;
         }
+        if !self.data.is_empty() {
+            os.write_string(3, &self.data)?;
+        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -4854,6 +4890,11 @@ impl ::protobuf::Message for Cell {
                 |m: &Cell| { &m.content },
                 |m: &mut Cell| { &mut m.content },
             ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "data",
+                |m: &Cell| { &m.data },
+                |m: &mut Cell| { &mut m.data },
+            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<Cell>(
                 "Cell",
                 fields,
@@ -4872,6 +4913,7 @@ impl ::protobuf::Clear for Cell {
     fn clear(&mut self) {
         self.field_id.clear();
         self.content.clear();
+        self.data.clear();
         self.unknown_fields.clear();
     }
 }
@@ -8001,46 +8043,46 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\x123\n\x0cupdated_rows\x18\
     \x04\x20\x03(\x0b2\x10.UpdatedRowOrderR\x0bupdatedRows\"E\n\tGridBlock\
     \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\
-    \x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08field_id\
+    \x20\x03(\x0b2\t.RowOrderR\trowOrders\"O\n\x04Cell\x12\x19\n\x08field_id\
     \x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\
-    \x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b\
-    2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\
-    \x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\
-    \x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01\
-    (\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\
-    \x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstart\
-    RowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12InsertFieldPayload\
-    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\
-    \x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\
-    \x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\
-    \x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\
-    \x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\
-    \x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\
-    \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\
-    \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\
-    \x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\x15FieldChangesetPayload\
-    \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\
-    id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\x01(\tH\0\
-    R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\x12+\n\nfie\
-    ld_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\x12\x18\n\x06\
-    frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvisibility\x18\
-    \x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\x08\x20\x01(\
-    \x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\x01(\x0cH\x06R\
-    \x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x13\n\x11one\
-    _of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_visibilityB\x0e\n\
-    \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\x01\n\x0fMoveIt\
-    emPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x17\n\
-    \x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_index\x18\x03\
-    \x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\x01(\x05R\
-    \x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTypeR\x02ty\
-    \"\x7f\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06grid\
-    Id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_i\
-    d\x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0\
-    R\x04dataB\r\n\x0bone_of_data**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\
-    \0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\
-    \0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0c\
-    SingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Check\
-    box\x10\x05b\x06proto3\
+    \x07content\x12\x12\n\x04data\x18\x03\x20\x01(\tR\x04data\"+\n\x0cRepeat\
+    edCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\
+    \x11CreateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\
+    \x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\
+    \x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10\
+    CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\
+    \"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of\
+    _start_row_id\"\xb6\x01\n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\
+    \x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.\
+    FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etype\
+    OptionData\x12&\n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\x0cstartField\
+    IdB\x17\n\x15one_of_start_field_id\"d\n\x11QueryFieldPayload\x12\x17\n\
+    \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_orders\x18\x02\
+    \x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\x16QueryGridB\
+    locksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x122\n\
+    \x0cblock_orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\x0bblockOrder\
+    s\"\xa8\x03\n\x15FieldChangesetPayload\x12\x19\n\x08field_id\x18\x01\x20\
+    \x01(\tR\x07fieldId\x12\x17\n\x07grid_id\x18\x02\x20\x01(\tR\x06gridId\
+    \x12\x14\n\x04name\x18\x03\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\
+    \x04\x20\x01(\tH\x01R\x04desc\x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n\
+    .FieldTypeH\x02R\tfieldType\x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\
+    \x03R\x06frozen\x12\x20\n\nvisibility\x18\x07\x20\x01(\x08H\x04R\nvisibi\
+    lity\x12\x16\n\x05width\x18\x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10ty\
+    pe_option_data\x18\t\x20\x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of\
+    _nameB\r\n\x0bone_of_descB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_fro\
+    zenB\x13\n\x11one_of_visibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_\
+    type_option_data\"\x9c\x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\
+    \x01\x20\x01(\tR\x06gridId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06i\
+    temId\x12\x1d\n\nfrom_index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\
+    \x08to_index\x18\x04\x20\x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\
+    \x20\x01(\x0e2\r.MoveItemTypeR\x02ty\"\x7f\n\rCellChangeset\x12\x17\n\
+    \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\
+    \x20\x01(\tR\x05rowId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07field\
+    Id\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0R\x04dataB\r\n\x0bone_of_data*\
+    *\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\x12\x0b\n\x07MoveRow\x10\
+    \x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\
+    \x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\x12\
+    \x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 1 - 0
shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

@@ -94,6 +94,7 @@ message GridBlock {
 message Cell {
     string field_id = 1;
     string content = 2;
+    string data = 3;
 }
 message RepeatedCell {
     repeated Cell items = 1;