mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #592 from AppFlowy-IO/feat/filter_cell
Apply filter with each cell
This commit is contained in:
commit
0977bfb14e
@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
|||||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||||
|
|
||||||
typedef GridBlockUpdateNotifierValue = Either<List<GridRowsChangeset>, FlowyError>;
|
typedef GridBlockUpdateNotifierValue = Either<List<GridBlockChangeset>, FlowyError>;
|
||||||
|
|
||||||
class GridBlockListener {
|
class GridBlockListener {
|
||||||
final String blockId;
|
final String blockId;
|
||||||
@ -33,7 +33,7 @@ class GridBlockListener {
|
|||||||
switch (ty) {
|
switch (ty) {
|
||||||
case GridNotification.DidUpdateGridBlock:
|
case GridNotification.DidUpdateGridBlock:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) => _rowsUpdateNotifier?.value = left([GridRowsChangeset.fromBuffer(payload)]),
|
(payload) => _rowsUpdateNotifier?.value = left([GridBlockChangeset.fromBuffer(payload)]),
|
||||||
(error) => _rowsUpdateNotifier?.value = right(error),
|
(error) => _rowsUpdateNotifier?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -43,7 +43,7 @@ class GridRowCacheService {
|
|||||||
_delegate = delegate {
|
_delegate = delegate {
|
||||||
//
|
//
|
||||||
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
|
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
|
||||||
_rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo)).toList();
|
_rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
@ -52,22 +52,24 @@ class GridRowCacheService {
|
|||||||
await _cellCache.dispose();
|
await _cellCache.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyChangesets(List<GridRowsChangeset> changesets) {
|
void applyChangesets(List<GridBlockChangeset> changesets) {
|
||||||
for (final changeset in changesets) {
|
for (final changeset in changesets) {
|
||||||
_deleteRows(changeset.deletedRows);
|
_deleteRows(changeset.deletedRows);
|
||||||
_insertRows(changeset.insertedRows);
|
_insertRows(changeset.insertedRows);
|
||||||
_updateRows(changeset.updatedRows);
|
_updateRows(changeset.updatedRows);
|
||||||
|
_hideRows(changeset.hideRows);
|
||||||
|
_showRows(changeset.visibleRows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deleteRows(List<GridRowId> deletedRows) {
|
void _deleteRows(List<String> deletedRows) {
|
||||||
if (deletedRows.isEmpty) {
|
if (deletedRows.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<GridRow> newRows = [];
|
final List<GridRow> newRows = [];
|
||||||
final DeletedIndexs deletedIndex = [];
|
final DeletedIndexs deletedIndex = [];
|
||||||
final Map<String, GridRowId> deletedRowByRowId = {for (var e in deletedRows) e.rowId: e};
|
final Map<String, String> deletedRowByRowId = {for (var rowId in deletedRows) rowId: rowId};
|
||||||
|
|
||||||
_rows.asMap().forEach((index, row) {
|
_rows.asMap().forEach((index, row) {
|
||||||
if (deletedRowByRowId[row.rowId] == null) {
|
if (deletedRowByRowId[row.rowId] == null) {
|
||||||
@ -80,7 +82,7 @@ class GridRowCacheService {
|
|||||||
_notifier.receive(GridRowChangeReason.delete(deletedIndex));
|
_notifier.receive(GridRowChangeReason.delete(deletedIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _insertRows(List<IndexRowOrder> insertRows) {
|
void _insertRows(List<InsertedRow> insertRows) {
|
||||||
if (insertRows.isEmpty) {
|
if (insertRows.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -90,16 +92,16 @@ class GridRowCacheService {
|
|||||||
for (final insertRow in insertRows) {
|
for (final insertRow in insertRows) {
|
||||||
final insertIndex = InsertedIndex(
|
final insertIndex = InsertedIndex(
|
||||||
index: insertRow.index,
|
index: insertRow.index,
|
||||||
rowId: insertRow.rowInfo.rowId,
|
rowId: insertRow.rowId,
|
||||||
);
|
);
|
||||||
insertIndexs.add(insertIndex);
|
insertIndexs.add(insertIndex);
|
||||||
newRows.insert(insertRow.index, (buildGridRow(insertRow.rowInfo)));
|
newRows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
|
||||||
}
|
}
|
||||||
|
|
||||||
_notifier.receive(GridRowChangeReason.insert(insertIndexs));
|
_notifier.receive(GridRowChangeReason.insert(insertIndexs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateRows(List<UpdatedRowOrder> updatedRows) {
|
void _updateRows(List<UpdatedRow> updatedRows) {
|
||||||
if (updatedRows.isEmpty) {
|
if (updatedRows.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -107,14 +109,13 @@ class GridRowCacheService {
|
|||||||
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
||||||
final List<GridRow> newRows = _rows;
|
final List<GridRow> newRows = _rows;
|
||||||
for (final updatedRow in updatedRows) {
|
for (final updatedRow in updatedRows) {
|
||||||
final rowOrder = updatedRow.rowInfo;
|
final rowId = updatedRow.rowId;
|
||||||
final rowId = updatedRow.rowInfo.rowId;
|
|
||||||
final index = newRows.indexWhere((row) => row.rowId == rowId);
|
final index = newRows.indexWhere((row) => row.rowId == rowId);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
_rowByRowId[rowId] = updatedRow.row;
|
_rowByRowId[rowId] = updatedRow.row;
|
||||||
|
|
||||||
newRows.removeAt(index);
|
newRows.removeAt(index);
|
||||||
newRows.insert(index, buildGridRow(rowOrder));
|
newRows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
|
||||||
updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
|
updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,6 +123,10 @@ class GridRowCacheService {
|
|||||||
_notifier.receive(GridRowChangeReason.update(updatedIndexs));
|
_notifier.receive(GridRowChangeReason.update(updatedIndexs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _hideRows(List<String> hideRows) {}
|
||||||
|
|
||||||
|
void _showRows(List<String> visibleRows) {}
|
||||||
|
|
||||||
void onRowsChanged(
|
void onRowsChanged(
|
||||||
void Function(GridRowChangeReason) onRowChanged,
|
void Function(GridRowChangeReason) onRowChanged,
|
||||||
) {
|
) {
|
||||||
@ -226,13 +231,13 @@ class GridRowCacheService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridRow buildGridRow(BlockRowInfo rowInfo) {
|
GridRow buildGridRow(String rowId, double rowHeight) {
|
||||||
return GridRow(
|
return GridRow(
|
||||||
gridId: gridId,
|
gridId: gridId,
|
||||||
blockId: block.id,
|
blockId: block.id,
|
||||||
fields: _delegate.fields,
|
fields: _delegate.fields,
|
||||||
rowId: rowInfo.rowId,
|
rowId: rowId,
|
||||||
height: rowInfo.height.toDouble(),
|
height: rowHeight,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ macro_rules! impl_def_and_def_mut {
|
|||||||
impl $target {
|
impl $target {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn into_inner(&mut self) -> Vec<$item> {
|
pub fn into_inner(&mut self) -> Vec<$item> {
|
||||||
::std::mem::replace(&mut self.items, vec![])
|
::std::mem::take(&mut self.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::entities::GridRowId;
|
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use flowy_error::ErrorCode;
|
use flowy_error::ErrorCode;
|
||||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||||
@ -11,11 +10,11 @@ pub struct GridBlock {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub row_infos: Vec<BlockRowInfo>,
|
pub row_infos: Vec<RowInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridBlock {
|
impl GridBlock {
|
||||||
pub fn new(block_id: &str, row_orders: Vec<BlockRowInfo>) -> Self {
|
pub fn new(block_id: &str, row_orders: Vec<RowInfo>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: block_id.to_owned(),
|
id: block_id.to_owned(),
|
||||||
row_infos: row_orders,
|
row_infos: row_orders,
|
||||||
@ -24,7 +23,7 @@ impl GridBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||||
pub struct BlockRowInfo {
|
pub struct RowInfo {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub block_id: String,
|
pub block_id: String,
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ pub struct BlockRowInfo {
|
|||||||
pub height: i32,
|
pub height: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockRowInfo {
|
impl RowInfo {
|
||||||
pub fn row_id(&self) -> &str {
|
pub fn row_id(&self) -> &str {
|
||||||
&self.row_id
|
&self.row_id
|
||||||
}
|
}
|
||||||
@ -45,7 +44,7 @@ impl BlockRowInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<&RowRevision> for BlockRowInfo {
|
impl std::convert::From<&RowRevision> for RowInfo {
|
||||||
fn from(rev: &RowRevision) -> Self {
|
fn from(rev: &RowRevision) -> Self {
|
||||||
Self {
|
Self {
|
||||||
block_id: rev.block_id.clone(),
|
block_id: rev.block_id.clone(),
|
||||||
@ -55,7 +54,7 @@ impl std::convert::From<&RowRevision> for BlockRowInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<&Arc<RowRevision>> for BlockRowInfo {
|
impl std::convert::From<&Arc<RowRevision>> for RowInfo {
|
||||||
fn from(rev: &Arc<RowRevision>) -> Self {
|
fn from(rev: &Arc<RowRevision>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
block_id: rev.block_id.clone(),
|
block_id: rev.block_id.clone(),
|
||||||
@ -104,84 +103,102 @@ impl std::convert::From<Vec<GridBlock>> for RepeatedGridBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||||
pub struct IndexRowOrder {
|
pub struct InsertedRow {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub row_info: BlockRowInfo,
|
pub block_id: String,
|
||||||
|
|
||||||
#[pb(index = 2, one_of)]
|
#[pb(index = 2)]
|
||||||
|
pub row_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 3)]
|
||||||
|
pub height: i32,
|
||||||
|
|
||||||
|
#[pb(index = 4, one_of)]
|
||||||
pub index: Option<i32>,
|
pub index: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, ProtoBuf)]
|
#[derive(Debug, Default, ProtoBuf)]
|
||||||
pub struct UpdatedRowOrder {
|
pub struct UpdatedRow {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub row_info: BlockRowInfo,
|
pub block_id: String,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
|
pub row_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 3)]
|
||||||
pub row: Row,
|
pub row: Row,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdatedRowOrder {
|
impl UpdatedRow {
|
||||||
pub fn new(row_rev: &RowRevision, row: Row) -> Self {
|
pub fn new(row_rev: &RowRevision, row: Row) -> Self {
|
||||||
Self {
|
Self {
|
||||||
row_info: BlockRowInfo::from(row_rev),
|
row_id: row_rev.id.clone(),
|
||||||
|
block_id: row_rev.block_id.clone(),
|
||||||
row,
|
row,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<BlockRowInfo> for IndexRowOrder {
|
impl std::convert::From<RowInfo> for InsertedRow {
|
||||||
fn from(row_info: BlockRowInfo) -> Self {
|
fn from(row_info: RowInfo) -> Self {
|
||||||
Self { row_info, index: None }
|
Self {
|
||||||
|
row_id: row_info.row_id,
|
||||||
|
block_id: row_info.block_id,
|
||||||
|
height: row_info.height,
|
||||||
|
index: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<&RowRevision> for IndexRowOrder {
|
impl std::convert::From<&RowRevision> for InsertedRow {
|
||||||
fn from(row: &RowRevision) -> Self {
|
fn from(row: &RowRevision) -> Self {
|
||||||
let row_order = BlockRowInfo::from(row);
|
let row_order = RowInfo::from(row);
|
||||||
Self::from(row_order)
|
Self::from(row_order)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, ProtoBuf)]
|
#[derive(Debug, Default, ProtoBuf)]
|
||||||
pub struct GridRowsChangeset {
|
pub struct GridBlockChangeset {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub block_id: String,
|
pub block_id: String,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub inserted_rows: Vec<IndexRowOrder>,
|
pub inserted_rows: Vec<InsertedRow>,
|
||||||
|
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub deleted_rows: Vec<GridRowId>,
|
pub deleted_rows: Vec<String>,
|
||||||
|
|
||||||
#[pb(index = 4)]
|
#[pb(index = 4)]
|
||||||
pub updated_rows: Vec<UpdatedRowOrder>,
|
pub updated_rows: Vec<UpdatedRow>,
|
||||||
|
|
||||||
|
#[pb(index = 5)]
|
||||||
|
pub visible_rows: Vec<String>,
|
||||||
|
|
||||||
|
#[pb(index = 6)]
|
||||||
|
pub hide_rows: Vec<String>,
|
||||||
}
|
}
|
||||||
impl GridRowsChangeset {
|
impl GridBlockChangeset {
|
||||||
pub fn insert(block_id: &str, inserted_rows: Vec<IndexRowOrder>) -> Self {
|
pub fn insert(block_id: &str, inserted_rows: Vec<InsertedRow>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
block_id: block_id.to_owned(),
|
block_id: block_id.to_owned(),
|
||||||
inserted_rows,
|
inserted_rows,
|
||||||
deleted_rows: vec![],
|
..Default::default()
|
||||||
updated_rows: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(block_id: &str, deleted_rows: Vec<GridRowId>) -> Self {
|
pub fn delete(block_id: &str, deleted_rows: Vec<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
block_id: block_id.to_owned(),
|
block_id: block_id.to_owned(),
|
||||||
inserted_rows: vec![],
|
|
||||||
deleted_rows,
|
deleted_rows,
|
||||||
updated_rows: vec![],
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowOrder>) -> Self {
|
pub fn update(block_id: &str, updated_rows: Vec<UpdatedRow>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
block_id: block_id.to_owned(),
|
block_id: block_id.to_owned(),
|
||||||
inserted_rows: vec![],
|
|
||||||
deleted_rows: vec![],
|
|
||||||
updated_rows,
|
updated_rows,
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ macro_rules! impl_type_option {
|
|||||||
($target: ident, $field_type:expr) => {
|
($target: ident, $field_type:expr) => {
|
||||||
impl std::convert::From<&FieldRevision> for $target {
|
impl std::convert::From<&FieldRevision> for $target {
|
||||||
fn from(field_rev: &FieldRevision) -> $target {
|
fn from(field_rev: &FieldRevision) -> $target {
|
||||||
match field_rev.get_type_option_entry::<$target, _>(&$field_type) {
|
match field_rev.get_type_option_entry::<$target>($field_type.into()) {
|
||||||
None => $target::default(),
|
None => $target::default(),
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ macro_rules! impl_type_option {
|
|||||||
|
|
||||||
impl std::convert::From<&std::sync::Arc<FieldRevision>> for $target {
|
impl std::convert::From<&std::sync::Arc<FieldRevision>> for $target {
|
||||||
fn from(field_rev: &std::sync::Arc<FieldRevision>) -> $target {
|
fn from(field_rev: &std::sync::Arc<FieldRevision>) -> $target {
|
||||||
match field_rev.get_type_option_entry::<$target, _>(&$field_type) {
|
match field_rev.get_type_option_entry::<$target>($field_type.into()) {
|
||||||
None => $target::default(),
|
None => $target::default(),
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||||
use crate::entities::{BlockRowInfo, CellChangeset, GridRowId, GridRowsChangeset, IndexRowOrder, Row, UpdatedRowOrder};
|
use crate::entities::{CellChangeset, GridBlockChangeset, InsertedRow, Row, RowInfo, UpdatedRow};
|
||||||
use crate::manager::GridUser;
|
use crate::manager::GridUser;
|
||||||
use crate::services::block_revision_editor::GridBlockRevisionEditor;
|
use crate::services::block_revision_editor::GridBlockRevisionEditor;
|
||||||
use crate::services::persistence::block_index::BlockIndexCache;
|
use crate::services::persistence::block_index::BlockIndexCache;
|
||||||
@ -71,12 +71,12 @@ impl GridBlockManager {
|
|||||||
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
||||||
let editor = self.get_editor(&row_rev.block_id).await?;
|
let editor = self.get_editor(&row_rev.block_id).await?;
|
||||||
|
|
||||||
let mut index_row_order = IndexRowOrder::from(&row_rev);
|
let mut index_row_order = InsertedRow::from(&row_rev);
|
||||||
let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
|
let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
|
||||||
index_row_order.index = row_index;
|
index_row_order.index = row_index;
|
||||||
|
|
||||||
let _ = self
|
let _ = self
|
||||||
.notify_did_update_block(block_id, GridRowsChangeset::insert(block_id, vec![index_row_order]))
|
.notify_did_update_block(block_id, GridBlockChangeset::insert(block_id, vec![index_row_order]))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(row_count)
|
Ok(row_count)
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ impl GridBlockManager {
|
|||||||
let mut row_count = 0;
|
let mut row_count = 0;
|
||||||
for row in row_revs {
|
for row in row_revs {
|
||||||
let _ = self.persistence.insert(&row.block_id, &row.id)?;
|
let _ = self.persistence.insert(&row.block_id, &row.id)?;
|
||||||
let mut row_order = IndexRowOrder::from(&row);
|
let mut row_order = InsertedRow::from(&row);
|
||||||
let (count, index) = editor.create_row(row, None).await?;
|
let (count, index) = editor.create_row(row, None).await?;
|
||||||
row_count = count;
|
row_count = count;
|
||||||
row_order.index = index;
|
row_order.index = index;
|
||||||
@ -101,7 +101,7 @@ impl GridBlockManager {
|
|||||||
changesets.push(GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count));
|
changesets.push(GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count));
|
||||||
|
|
||||||
let _ = self
|
let _ = self
|
||||||
.notify_did_update_block(&block_id, GridRowsChangeset::insert(&block_id, inserted_row_orders))
|
.notify_did_update_block(&block_id, GridBlockChangeset::insert(&block_id, inserted_row_orders))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,8 +118,8 @@ impl GridBlockManager {
|
|||||||
None => tracing::error!("Internal error: can't find the row with id: {}", changeset.row_id),
|
None => tracing::error!("Internal error: can't find the row with id: {}", changeset.row_id),
|
||||||
Some(row_rev) => {
|
Some(row_rev) => {
|
||||||
if let Some(row) = row_builder(row_rev.clone()) {
|
if let Some(row) = row_builder(row_rev.clone()) {
|
||||||
let row_order = UpdatedRowOrder::new(&row_rev, row);
|
let row_order = UpdatedRow::new(&row_rev, row);
|
||||||
let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]);
|
let block_order_changeset = GridBlockChangeset::update(&editor.block_id, vec![row_order]);
|
||||||
let _ = self
|
let _ = self
|
||||||
.notify_did_update_block(&editor.block_id, block_order_changeset)
|
.notify_did_update_block(&editor.block_id, block_order_changeset)
|
||||||
.await?;
|
.await?;
|
||||||
@ -137,14 +137,8 @@ impl GridBlockManager {
|
|||||||
None => {}
|
None => {}
|
||||||
Some(row_info) => {
|
Some(row_info) => {
|
||||||
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
|
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
|
||||||
|
|
||||||
let row_identifier = GridRowId {
|
|
||||||
grid_id: self.grid_id.clone(),
|
|
||||||
block_id: row_info.block_id,
|
|
||||||
row_id: row_info.row_id,
|
|
||||||
};
|
|
||||||
let _ = self
|
let _ = self
|
||||||
.notify_did_update_block(&block_id, GridRowsChangeset::delete(&block_id, vec![row_identifier]))
|
.notify_did_update_block(&block_id, GridBlockChangeset::delete(&block_id, vec![row_info.row_id]))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +148,7 @@ impl GridBlockManager {
|
|||||||
|
|
||||||
pub(crate) async fn delete_rows(
|
pub(crate) async fn delete_rows(
|
||||||
&self,
|
&self,
|
||||||
row_orders: Vec<BlockRowInfo>,
|
row_orders: Vec<RowInfo>,
|
||||||
) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
|
) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
for grid_block in block_from_row_orders(row_orders) {
|
for grid_block in block_from_row_orders(row_orders) {
|
||||||
@ -179,22 +173,18 @@ impl GridBlockManager {
|
|||||||
match editor.get_row_revs(Some(vec![Cow::Borrowed(row_id)])).await?.pop() {
|
match editor.get_row_revs(Some(vec![Cow::Borrowed(row_id)])).await?.pop() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(row_rev) => {
|
Some(row_rev) => {
|
||||||
let row_info = BlockRowInfo::from(&row_rev);
|
let insert_row = InsertedRow {
|
||||||
let insert_row = IndexRowOrder {
|
block_id: row_rev.block_id.clone(),
|
||||||
row_info: row_info.clone(),
|
row_id: row_rev.id.clone(),
|
||||||
index: Some(to as i32),
|
index: Some(to as i32),
|
||||||
|
height: row_rev.height,
|
||||||
};
|
};
|
||||||
|
|
||||||
let deleted_row = GridRowId {
|
let notified_changeset = GridBlockChangeset {
|
||||||
grid_id: self.grid_id.clone(),
|
|
||||||
block_id: row_info.block_id,
|
|
||||||
row_id: row_info.row_id,
|
|
||||||
};
|
|
||||||
let notified_changeset = GridRowsChangeset {
|
|
||||||
block_id: editor.block_id.clone(),
|
block_id: editor.block_id.clone(),
|
||||||
inserted_rows: vec![insert_row],
|
inserted_rows: vec![insert_row],
|
||||||
deleted_rows: vec![deleted_row],
|
deleted_rows: vec![row_rev.id.clone()],
|
||||||
updated_rows: vec![],
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = self
|
let _ = self
|
||||||
@ -227,7 +217,7 @@ impl GridBlockManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_row_orders(&self, block_id: &str) -> FlowyResult<Vec<BlockRowInfo>> {
|
pub async fn get_row_orders(&self, block_id: &str) -> FlowyResult<Vec<RowInfo>> {
|
||||||
let editor = self.get_editor(block_id).await?;
|
let editor = self.get_editor(block_id).await?;
|
||||||
editor.get_row_infos::<&str>(None).await
|
editor.get_row_infos::<&str>(None).await
|
||||||
}
|
}
|
||||||
@ -257,7 +247,7 @@ impl GridBlockManager {
|
|||||||
Ok(snapshots)
|
Ok(snapshots)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn notify_did_update_block(&self, block_id: &str, changeset: GridRowsChangeset) -> FlowyResult<()> {
|
async fn notify_did_update_block(&self, block_id: &str, changeset: GridBlockChangeset) -> FlowyResult<()> {
|
||||||
send_dart_notification(block_id, GridNotification::DidUpdateGridBlock)
|
send_dart_notification(block_id, GridNotification::DidUpdateGridBlock)
|
||||||
.payload(changeset)
|
.payload(changeset)
|
||||||
.send();
|
.send();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::entities::BlockRowInfo;
|
use crate::entities::RowInfo;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision};
|
use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision};
|
||||||
@ -123,12 +123,12 @@ impl GridBlockRevisionEditor {
|
|||||||
Ok(cell_revs)
|
Ok(cell_revs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_row_info(&self, row_id: &str) -> FlowyResult<Option<BlockRowInfo>> {
|
pub async fn get_row_info(&self, row_id: &str) -> FlowyResult<Option<RowInfo>> {
|
||||||
let row_ids = Some(vec![Cow::Borrowed(row_id)]);
|
let row_ids = Some(vec![Cow::Borrowed(row_id)]);
|
||||||
Ok(self.get_row_infos(row_ids).await?.pop())
|
Ok(self.get_row_infos(row_ids).await?.pop())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_row_infos<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<BlockRowInfo>>
|
pub async fn get_row_infos<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<RowInfo>>
|
||||||
where
|
where
|
||||||
T: AsRef<str> + ToOwned + ?Sized,
|
T: AsRef<str> + ToOwned + ?Sized,
|
||||||
{
|
{
|
||||||
@ -138,8 +138,8 @@ impl GridBlockRevisionEditor {
|
|||||||
.await
|
.await
|
||||||
.get_row_revs(row_ids)?
|
.get_row_revs(row_ids)?
|
||||||
.iter()
|
.iter()
|
||||||
.map(BlockRowInfo::from)
|
.map(RowInfo::from)
|
||||||
.collect::<Vec<BlockRowInfo>>();
|
.collect::<Vec<RowInfo>>();
|
||||||
Ok(row_infos)
|
Ok(row_infos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::entities::{FieldType, GridCheckboxFilter};
|
use crate::entities::{FieldType, GridCheckboxFilter};
|
||||||
use crate::impl_type_option;
|
use crate::impl_type_option;
|
||||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData};
|
use crate::services::row::{
|
||||||
|
AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
@ -40,10 +42,16 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
|
|||||||
const YES: &str = "Yes";
|
const YES: &str = "Yes";
|
||||||
const NO: &str = "No";
|
const NO: &str = "No";
|
||||||
|
|
||||||
impl CellDataOperation<String, GridCheckboxFilter> for CheckboxTypeOption {
|
impl CellFilterOperation<GridCheckboxFilter, CheckboxCellData> for CheckboxTypeOption {
|
||||||
|
fn apply_filter(&self, _cell_data: CheckboxCellData, _filter: &GridCheckboxFilter) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellDataOperation<String> for CheckboxTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -54,7 +62,7 @@ impl CellDataOperation<String, GridCheckboxFilter> for CheckboxTypeOption {
|
|||||||
return Ok(DecodedCellData::default());
|
return Ok(DecodedCellData::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let encoded_data = encoded_data.into();
|
let encoded_data = cell_data.into();
|
||||||
if encoded_data == YES || encoded_data == NO {
|
if encoded_data == YES || encoded_data == NO {
|
||||||
return Ok(DecodedCellData::new(encoded_data));
|
return Ok(DecodedCellData::new(encoded_data));
|
||||||
}
|
}
|
||||||
@ -62,10 +70,6 @@ impl CellDataOperation<String, GridCheckboxFilter> for CheckboxTypeOption {
|
|||||||
Ok(DecodedCellData::default())
|
Ok(DecodedCellData::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridCheckboxFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
C: Into<CellContentChangeset>,
|
C: Into<CellContentChangeset>,
|
||||||
@ -92,12 +96,19 @@ fn string_to_bool(bool_str: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CheckboxCellData(String);
|
||||||
|
impl std::convert::From<AnyCellData> for CheckboxCellData {
|
||||||
|
fn from(any_cell_data: AnyCellData) -> Self {
|
||||||
|
CheckboxCellData(any_cell_data.cell_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::services::field::type_options::checkbox_type_option::{NO, YES};
|
use crate::services::field::type_options::checkbox_type_option::{NO, YES};
|
||||||
|
|
||||||
use crate::services::field::FieldBuilder;
|
use crate::services::field::FieldBuilder;
|
||||||
use crate::services::row::{apply_cell_data_changeset, decode_cell_data};
|
use crate::services::row::{apply_cell_data_changeset, decode_any_cell_data};
|
||||||
|
|
||||||
use crate::entities::FieldType;
|
use crate::entities::FieldType;
|
||||||
|
|
||||||
@ -105,21 +116,21 @@ mod tests {
|
|||||||
fn checkout_box_description_test() {
|
fn checkout_box_description_test() {
|
||||||
let field_rev = FieldBuilder::from_field_type(&FieldType::Checkbox).build();
|
let field_rev = FieldBuilder::from_field_type(&FieldType::Checkbox).build();
|
||||||
let data = apply_cell_data_changeset("true", None, &field_rev).unwrap();
|
let data = apply_cell_data_changeset("true", None, &field_rev).unwrap();
|
||||||
assert_eq!(decode_cell_data(data, &field_rev).to_string(), YES);
|
assert_eq!(decode_any_cell_data(data, &field_rev).to_string(), YES);
|
||||||
|
|
||||||
let data = apply_cell_data_changeset("1", None, &field_rev).unwrap();
|
let data = apply_cell_data_changeset("1", None, &field_rev).unwrap();
|
||||||
assert_eq!(decode_cell_data(data, &field_rev,).to_string(), YES);
|
assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), YES);
|
||||||
|
|
||||||
let data = apply_cell_data_changeset("yes", None, &field_rev).unwrap();
|
let data = apply_cell_data_changeset("yes", None, &field_rev).unwrap();
|
||||||
assert_eq!(decode_cell_data(data, &field_rev,).to_string(), YES);
|
assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), YES);
|
||||||
|
|
||||||
let data = apply_cell_data_changeset("false", None, &field_rev).unwrap();
|
let data = apply_cell_data_changeset("false", None, &field_rev).unwrap();
|
||||||
assert_eq!(decode_cell_data(data, &field_rev,).to_string(), NO);
|
assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO);
|
||||||
|
|
||||||
let data = apply_cell_data_changeset("no", None, &field_rev).unwrap();
|
let data = apply_cell_data_changeset("no", None, &field_rev).unwrap();
|
||||||
assert_eq!(decode_cell_data(data, &field_rev,).to_string(), NO);
|
assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO);
|
||||||
|
|
||||||
let data = apply_cell_data_changeset("12", None, &field_rev).unwrap();
|
let data = apply_cell_data_changeset("12", None, &field_rev).unwrap();
|
||||||
assert_eq!(decode_cell_data(data, &field_rev,).to_string(), NO);
|
assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ use crate::entities::{CellChangeset, FieldType, GridDateFilter};
|
|||||||
use crate::entities::{CellIdentifier, CellIdentifierPayload};
|
use crate::entities::{CellIdentifier, CellIdentifierPayload};
|
||||||
use crate::impl_type_option;
|
use crate::impl_type_option;
|
||||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData};
|
use crate::services::row::{
|
||||||
|
AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use chrono::format::strftime::StrftimeItems;
|
use chrono::format::strftime::StrftimeItems;
|
||||||
use chrono::{NaiveDateTime, Timelike};
|
use chrono::{NaiveDateTime, Timelike};
|
||||||
@ -115,10 +117,16 @@ impl DateTypeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CellDataOperation<String, GridDateFilter> for DateTypeOption {
|
impl CellFilterOperation<GridDateFilter, AnyCellData> for DateTypeOption {
|
||||||
|
fn apply_filter(&self, _cell_data: AnyCellData, _filter: &GridDateFilter) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellDataOperation<String> for DateTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -133,15 +141,11 @@ impl CellDataOperation<String, GridDateFilter> for DateTypeOption {
|
|||||||
return Ok(DecodedCellData::default());
|
return Ok(DecodedCellData::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let timestamp = encoded_data.into().parse::<i64>().unwrap_or(0);
|
let timestamp = cell_data.into().parse::<i64>().unwrap_or(0);
|
||||||
let date = self.today_desc_from_timestamp(timestamp);
|
let date = self.today_desc_from_timestamp(timestamp);
|
||||||
DecodedCellData::try_from_bytes(date)
|
DecodedCellData::try_from_bytes(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridDateFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
C: Into<CellContentChangeset>,
|
C: Into<CellContentChangeset>,
|
||||||
|
@ -3,7 +3,9 @@ use crate::impl_type_option;
|
|||||||
use crate::entities::{FieldType, GridNumberFilter};
|
use crate::entities::{FieldType, GridNumberFilter};
|
||||||
use crate::services::field::type_options::number_type_option::format::*;
|
use crate::services::field::type_options::number_type_option::format::*;
|
||||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData};
|
use crate::services::row::{
|
||||||
|
AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
@ -138,11 +140,17 @@ impl NumberTypeOption {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CellFilterOperation<GridNumberFilter, AnyCellData> for NumberTypeOption {
|
||||||
|
fn apply_filter(&self, any_cell_data: AnyCellData, _filter: &GridNumberFilter) -> bool {
|
||||||
|
let _number_cell_data = NumberCellData::from_number_type_option(self, any_cell_data);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CellDataOperation<String, GridNumberFilter> for NumberTypeOption {
|
impl CellDataOperation<String> for NumberTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -153,7 +161,7 @@ impl CellDataOperation<String, GridNumberFilter> for NumberTypeOption {
|
|||||||
return Ok(DecodedCellData::default());
|
return Ok(DecodedCellData::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let cell_data = encoded_data.into();
|
let cell_data = cell_data.into();
|
||||||
match self.format {
|
match self.format {
|
||||||
NumberFormat::Num => {
|
NumberFormat::Num => {
|
||||||
if let Ok(v) = cell_data.parse::<f64>() {
|
if let Ok(v) = cell_data.parse::<f64>() {
|
||||||
@ -179,10 +187,6 @@ impl CellDataOperation<String, GridNumberFilter> for NumberTypeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridNumberFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
C: Into<CellContentChangeset>,
|
C: Into<CellContentChangeset>,
|
||||||
@ -208,6 +212,38 @@ impl std::default::Default for NumberTypeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NumberCellData(String);
|
||||||
|
|
||||||
|
impl NumberCellData {
|
||||||
|
fn from_number_type_option(type_option: &NumberTypeOption, any_cell_data: AnyCellData) -> Self {
|
||||||
|
let cell_data = any_cell_data.cell_data;
|
||||||
|
match type_option.format {
|
||||||
|
NumberFormat::Num => {
|
||||||
|
if let Ok(v) = cell_data.parse::<f64>() {
|
||||||
|
return Self(v.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(v) = cell_data.parse::<i64>() {
|
||||||
|
return Self(v.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
NumberFormat::Percent => {
|
||||||
|
let content = cell_data.parse::<f64>().map_or(String::new(), |v| v.to_string());
|
||||||
|
Self(content)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let content = type_option
|
||||||
|
.money_from_number_str(&cell_data)
|
||||||
|
.unwrap_or_else(|_| "".to_string());
|
||||||
|
Self(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::entities::FieldType;
|
use crate::entities::FieldType;
|
||||||
|
@ -3,7 +3,9 @@ use crate::entities::{CellIdentifier, CellIdentifierPayload};
|
|||||||
use crate::impl_type_option;
|
use crate::impl_type_option;
|
||||||
use crate::services::field::type_options::util::get_cell_data;
|
use crate::services::field::type_options::util::get_cell_data;
|
||||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
|
use crate::services::row::{
|
||||||
|
AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
@ -95,10 +97,16 @@ impl SelectOptionOperation for SingleSelectTypeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CellDataOperation<String, GridSelectOptionFilter> for SingleSelectTypeOption {
|
impl CellFilterOperation<GridSelectOptionFilter, SelectOptionIds> for SingleSelectTypeOption {
|
||||||
|
fn apply_filter(&self, _cell_data: SelectOptionIds, _filter: &GridSelectOptionFilter) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellDataOperation<String> for SingleSelectTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -109,7 +117,7 @@ impl CellDataOperation<String, GridSelectOptionFilter> for SingleSelectTypeOptio
|
|||||||
return Ok(DecodedCellData::default());
|
return Ok(DecodedCellData::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let encoded_data = encoded_data.into();
|
let encoded_data = cell_data.into();
|
||||||
let mut cell_data = SelectOptionCellData {
|
let mut cell_data = SelectOptionCellData {
|
||||||
options: self.options.clone(),
|
options: self.options.clone(),
|
||||||
select_options: vec![],
|
select_options: vec![],
|
||||||
@ -123,10 +131,6 @@ impl CellDataOperation<String, GridSelectOptionFilter> for SingleSelectTypeOptio
|
|||||||
DecodedCellData::try_from_bytes(cell_data)
|
DecodedCellData::try_from_bytes(cell_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridSelectOptionFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
C: Into<CellContentChangeset>,
|
C: Into<CellContentChangeset>,
|
||||||
@ -196,11 +200,15 @@ impl SelectOptionOperation for MultiSelectTypeOption {
|
|||||||
&mut self.options
|
&mut self.options
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CellFilterOperation<GridSelectOptionFilter, SelectOptionIds> for MultiSelectTypeOption {
|
||||||
impl CellDataOperation<String, GridSelectOptionFilter> for MultiSelectTypeOption {
|
fn apply_filter(&self, _cell_data: SelectOptionIds, _filter: &GridSelectOptionFilter) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CellDataOperation<String> for MultiSelectTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -211,7 +219,7 @@ impl CellDataOperation<String, GridSelectOptionFilter> for MultiSelectTypeOption
|
|||||||
return Ok(DecodedCellData::default());
|
return Ok(DecodedCellData::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let encoded_data = encoded_data.into();
|
let encoded_data = cell_data.into();
|
||||||
let select_options = select_option_ids(encoded_data)
|
let select_options = select_option_ids(encoded_data)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|option_id| self.options.iter().find(|option| option.id == option_id).cloned())
|
.flat_map(|option_id| self.options.iter().find(|option| option.id == option_id).cloned())
|
||||||
@ -225,10 +233,6 @@ impl CellDataOperation<String, GridSelectOptionFilter> for MultiSelectTypeOption
|
|||||||
DecodedCellData::try_from_bytes(cell_data)
|
DecodedCellData::try_from_bytes(cell_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridSelectOptionFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<T>(&self, changeset: T, cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<T>(&self, changeset: T, cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
T: Into<CellContentChangeset>,
|
T: Into<CellContentChangeset>,
|
||||||
@ -286,6 +290,14 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SelectOptionIds(Vec<String>);
|
||||||
|
impl std::convert::From<AnyCellData> for SelectOptionIds {
|
||||||
|
fn from(any_cell_data: AnyCellData) -> Self {
|
||||||
|
let ids = select_option_ids(any_cell_data.cell_data);
|
||||||
|
Self(ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn select_option_ids(data: String) -> Vec<String> {
|
fn select_option_ids(data: String) -> Vec<String> {
|
||||||
data.split(SELECTION_IDS_SEPARATOR)
|
data.split(SELECTION_IDS_SEPARATOR)
|
||||||
.map(|id| id.to_string())
|
.map(|id| id.to_string())
|
||||||
@ -497,8 +509,8 @@ fn make_select_context_from(cell_rev: &Option<CellRevision>, options: &[SelectOp
|
|||||||
match cell_rev {
|
match cell_rev {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
Some(cell_rev) => {
|
Some(cell_rev) => {
|
||||||
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&cell_rev.data) {
|
if let Ok(type_option_cell_data) = AnyCellData::from_str(&cell_rev.data) {
|
||||||
select_option_ids(type_option_cell_data.data)
|
select_option_ids(type_option_cell_data.cell_data)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|option_id| options.iter().find(|option| option.id == option_id).cloned())
|
.flat_map(|option_id| options.iter().find(|option| option.id == option_id).cloned())
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::entities::{FieldType, GridTextFilter};
|
use crate::entities::{FieldType, GridTextFilter};
|
||||||
use crate::impl_type_option;
|
use crate::impl_type_option;
|
||||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||||
use crate::services::row::{try_decode_cell_data, CellContentChangeset, CellDataOperation, DecodedCellData};
|
use crate::services::row::{
|
||||||
|
try_decode_cell_data, AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
@ -30,10 +32,16 @@ pub struct RichTextTypeOption {
|
|||||||
}
|
}
|
||||||
impl_type_option!(RichTextTypeOption, FieldType::RichText);
|
impl_type_option!(RichTextTypeOption, FieldType::RichText);
|
||||||
|
|
||||||
impl CellDataOperation<String, GridTextFilter> for RichTextTypeOption {
|
impl CellFilterOperation<GridTextFilter, TextCellData> for RichTextTypeOption {
|
||||||
|
fn apply_filter(&self, _cell_data: TextCellData, _filter: &GridTextFilter) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellDataOperation<String> for RichTextTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -45,17 +53,13 @@ impl CellDataOperation<String, GridTextFilter> for RichTextTypeOption {
|
|||||||
|| decoded_field_type.is_multi_select()
|
|| decoded_field_type.is_multi_select()
|
||||||
|| decoded_field_type.is_number()
|
|| decoded_field_type.is_number()
|
||||||
{
|
{
|
||||||
try_decode_cell_data(encoded_data.into(), field_rev, decoded_field_type, decoded_field_type)
|
try_decode_cell_data(cell_data.into(), field_rev, decoded_field_type, decoded_field_type)
|
||||||
} else {
|
} else {
|
||||||
let cell_data = encoded_data.into();
|
let cell_data = cell_data.into();
|
||||||
Ok(DecodedCellData::new(cell_data))
|
Ok(DecodedCellData::new(cell_data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridTextFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
C: Into<CellContentChangeset>,
|
C: Into<CellContentChangeset>,
|
||||||
@ -69,6 +73,13 @@ impl CellDataOperation<String, GridTextFilter> for RichTextTypeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TextCellData(String);
|
||||||
|
impl std::convert::From<AnyCellData> for TextCellData {
|
||||||
|
fn from(any_data: AnyCellData) -> Self {
|
||||||
|
TextCellData(any_data.cell_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::entities::FieldType;
|
use crate::entities::FieldType;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::entities::{FieldType, GridTextFilter};
|
use crate::entities::{FieldType, GridTextFilter};
|
||||||
use crate::impl_type_option;
|
use crate::impl_type_option;
|
||||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
|
use crate::services::row::{
|
||||||
|
AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData, EncodedCellData,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
@ -33,10 +35,16 @@ pub struct URLTypeOption {
|
|||||||
}
|
}
|
||||||
impl_type_option!(URLTypeOption, FieldType::URL);
|
impl_type_option!(URLTypeOption, FieldType::URL);
|
||||||
|
|
||||||
impl CellDataOperation<EncodedCellData<URLCellData>, GridTextFilter> for URLTypeOption {
|
impl CellFilterOperation<GridTextFilter, URLCellData> for URLTypeOption {
|
||||||
|
fn apply_filter(&self, _cell_data: URLCellData, _filter: &GridTextFilter) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
@ -46,14 +54,10 @@ impl CellDataOperation<EncodedCellData<URLCellData>, GridTextFilter> for URLType
|
|||||||
if !decoded_field_type.is_url() {
|
if !decoded_field_type.is_url() {
|
||||||
return Ok(DecodedCellData::default());
|
return Ok(DecodedCellData::default());
|
||||||
}
|
}
|
||||||
let cell_data = encoded_data.into().try_into_inner()?;
|
let cell_data = cell_data.into().try_into_inner()?;
|
||||||
DecodedCellData::try_from_bytes(cell_data)
|
DecodedCellData::try_from_bytes(cell_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_filter(&self, _filter: GridTextFilter) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||||
where
|
where
|
||||||
C: Into<CellContentChangeset>,
|
C: Into<CellContentChangeset>,
|
||||||
@ -117,6 +121,12 @@ impl FromStr for URLCellData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<AnyCellData> for URLCellData {
|
||||||
|
fn from(any_cell_data: AnyCellData) -> Self {
|
||||||
|
URLCellData::from_str(&any_cell_data.cell_data).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref URL_REGEX: Regex = Regex::new(
|
static ref URL_REGEX: Regex = Regex::new(
|
||||||
"[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
"[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::services::row::TypeOptionCellData;
|
use crate::services::row::AnyCellData;
|
||||||
use flowy_grid_data_model::revision::CellRevision;
|
use flowy_grid_data_model::revision::CellRevision;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub fn get_cell_data(cell_rev: &CellRevision) -> String {
|
pub fn get_cell_data(cell_rev: &CellRevision) -> String {
|
||||||
match TypeOptionCellData::from_str(&cell_rev.data) {
|
match AnyCellData::from_str(&cell_rev.data) {
|
||||||
Ok(type_option) => type_option.data,
|
Ok(type_option) => type_option.cell_data,
|
||||||
Err(_) => String::new(),
|
Err(_) => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
161
frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs
Normal file
161
frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
use crate::entities::{
|
||||||
|
FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter, GridTextFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
use dashmap::DashMap;
|
||||||
|
|
||||||
|
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||||
|
use flowy_sync::client_grid::GridRevisionPad;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct FilterResultCache {
|
||||||
|
// key: row id
|
||||||
|
inner: DashMap<String, FilterResult>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterResultCache {
|
||||||
|
pub fn new() -> Arc<Self> {
|
||||||
|
let this = Self::default();
|
||||||
|
Arc::new(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for FilterResultCache {
|
||||||
|
type Target = DashMap<String, FilterResult>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct FilterResult {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) row_index: i32,
|
||||||
|
pub(crate) visible_by_field_id: HashMap<FilterId, bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterResult {
|
||||||
|
pub(crate) fn new(index: i32, _row_rev: &RowRevision) -> Self {
|
||||||
|
Self {
|
||||||
|
row_index: index,
|
||||||
|
visible_by_field_id: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_visible(&self) -> bool {
|
||||||
|
for visible in self.visible_by_field_id.values() {
|
||||||
|
if visible == &false {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct FilterCache {
|
||||||
|
pub(crate) text_filter: DashMap<FilterId, GridTextFilter>,
|
||||||
|
pub(crate) url_filter: DashMap<FilterId, GridTextFilter>,
|
||||||
|
pub(crate) number_filter: DashMap<FilterId, GridNumberFilter>,
|
||||||
|
pub(crate) date_filter: DashMap<FilterId, GridDateFilter>,
|
||||||
|
pub(crate) select_option_filter: DashMap<FilterId, GridSelectOptionFilter>,
|
||||||
|
pub(crate) checkbox_filter: DashMap<FilterId, GridCheckboxFilter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterCache {
|
||||||
|
pub(crate) async fn from_grid_pad(grid_pad: &Arc<RwLock<GridRevisionPad>>) -> Arc<Self> {
|
||||||
|
let this = Arc::new(Self::default());
|
||||||
|
let _ = reload_filter_cache(this.clone(), None, grid_pad).await;
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove(&self, filter_id: &FilterId) {
|
||||||
|
let _ = match filter_id.field_type {
|
||||||
|
FieldType::RichText => {
|
||||||
|
let _ = self.text_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
FieldType::Number => {
|
||||||
|
let _ = self.number_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
FieldType::DateTime => {
|
||||||
|
let _ = self.date_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
FieldType::SingleSelect => {
|
||||||
|
let _ = self.select_option_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
FieldType::MultiSelect => {
|
||||||
|
let _ = self.select_option_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
FieldType::Checkbox => {
|
||||||
|
let _ = self.checkbox_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
FieldType::URL => {
|
||||||
|
let _ = self.url_filter.remove(filter_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn reload_filter_cache(
|
||||||
|
cache: Arc<FilterCache>,
|
||||||
|
field_ids: Option<Vec<String>>,
|
||||||
|
grid_pad: &Arc<RwLock<GridRevisionPad>>,
|
||||||
|
) {
|
||||||
|
let grid_pad = grid_pad.read().await;
|
||||||
|
let filters_revs = grid_pad.get_filters(None, field_ids).unwrap_or_default();
|
||||||
|
|
||||||
|
for filter_rev in filters_revs {
|
||||||
|
match grid_pad.get_field_rev(&filter_rev.field_id) {
|
||||||
|
None => {}
|
||||||
|
Some((_, field_rev)) => {
|
||||||
|
let filter_id = FilterId::from(field_rev);
|
||||||
|
let field_type: FieldType = field_rev.field_type_rev.into();
|
||||||
|
match &field_type {
|
||||||
|
FieldType::RichText => {
|
||||||
|
let _ = cache.text_filter.insert(filter_id, GridTextFilter::from(filter_rev));
|
||||||
|
}
|
||||||
|
FieldType::Number => {
|
||||||
|
let _ = cache
|
||||||
|
.number_filter
|
||||||
|
.insert(filter_id, GridNumberFilter::from(filter_rev));
|
||||||
|
}
|
||||||
|
FieldType::DateTime => {
|
||||||
|
let _ = cache.date_filter.insert(filter_id, GridDateFilter::from(filter_rev));
|
||||||
|
}
|
||||||
|
FieldType::SingleSelect | FieldType::MultiSelect => {
|
||||||
|
let _ = cache
|
||||||
|
.select_option_filter
|
||||||
|
.insert(filter_id, GridSelectOptionFilter::from(filter_rev));
|
||||||
|
}
|
||||||
|
FieldType::Checkbox => {
|
||||||
|
let _ = cache
|
||||||
|
.checkbox_filter
|
||||||
|
.insert(filter_id, GridCheckboxFilter::from(filter_rev));
|
||||||
|
}
|
||||||
|
FieldType::URL => {
|
||||||
|
let _ = cache.url_filter.insert(filter_id, GridTextFilter::from(filter_rev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
|
pub(crate) struct FilterId {
|
||||||
|
pub(crate) field_id: String,
|
||||||
|
pub(crate) field_type: FieldType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<&Arc<FieldRevision>> for FilterId {
|
||||||
|
fn from(rev: &Arc<FieldRevision>) -> Self {
|
||||||
|
Self {
|
||||||
|
field_id: rev.id.clone(),
|
||||||
|
field_type: rev.field_type_rev.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,23 @@
|
|||||||
use crate::entities::{
|
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||||
FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter, GridTextFilter,
|
use crate::entities::{FieldType, GridBlockChangeset};
|
||||||
};
|
|
||||||
use crate::services::block_manager::GridBlockManager;
|
use crate::services::block_manager::GridBlockManager;
|
||||||
|
use crate::services::field::{
|
||||||
|
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
|
||||||
|
SingleSelectTypeOption, URLTypeOption,
|
||||||
|
};
|
||||||
|
use crate::services::filter::filter_cache::{
|
||||||
|
reload_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
|
||||||
|
};
|
||||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||||
use crate::services::row::GridBlockSnapshot;
|
use crate::services::row::{AnyCellData, CellFilterOperation, GridBlockSnapshot};
|
||||||
use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
|
use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision};
|
use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision};
|
||||||
use flowy_sync::client_grid::GridRevisionPad;
|
use flowy_sync::client_grid::GridRevisionPad;
|
||||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@ -19,8 +28,8 @@ pub(crate) struct GridFilterService {
|
|||||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||||
grid_pad: Arc<RwLock<GridRevisionPad>>,
|
grid_pad: Arc<RwLock<GridRevisionPad>>,
|
||||||
block_manager: Arc<GridBlockManager>,
|
block_manager: Arc<GridBlockManager>,
|
||||||
filter_cache: Arc<RwLock<FilterCache>>,
|
filter_cache: Arc<FilterCache>,
|
||||||
filter_result_cache: Arc<RwLock<FilterResultCache>>,
|
filter_result_cache: Arc<FilterResultCache>,
|
||||||
}
|
}
|
||||||
impl GridFilterService {
|
impl GridFilterService {
|
||||||
pub async fn new<S: GridServiceTaskScheduler>(
|
pub async fn new<S: GridServiceTaskScheduler>(
|
||||||
@ -29,13 +38,14 @@ impl GridFilterService {
|
|||||||
scheduler: S,
|
scheduler: S,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let grid_id = grid_pad.read().await.grid_id();
|
let grid_id = grid_pad.read().await.grid_id();
|
||||||
let filter_cache = Arc::new(RwLock::new(FilterCache::from_grid_pad(&grid_pad).await));
|
let scheduler = Arc::new(scheduler);
|
||||||
let filter_result_cache = Arc::new(RwLock::new(FilterResultCache::default()));
|
let filter_cache = FilterCache::from_grid_pad(&grid_pad).await;
|
||||||
|
let filter_result_cache = FilterResultCache::new();
|
||||||
Self {
|
Self {
|
||||||
grid_id,
|
grid_id,
|
||||||
grid_pad,
|
grid_pad,
|
||||||
block_manager,
|
block_manager,
|
||||||
scheduler: Arc::new(scheduler),
|
scheduler,
|
||||||
filter_cache,
|
filter_cache,
|
||||||
filter_result_cache,
|
filter_result_cache,
|
||||||
}
|
}
|
||||||
@ -51,20 +61,42 @@ impl GridFilterService {
|
|||||||
.map(|field_rev| (field_rev.id.clone(), field_rev))
|
.map(|field_rev| (field_rev.id.clone(), field_rev))
|
||||||
.collect::<HashMap<String, Arc<FieldRevision>>>();
|
.collect::<HashMap<String, Arc<FieldRevision>>>();
|
||||||
|
|
||||||
let mut show_rows = vec![];
|
let mut changesets = vec![];
|
||||||
let mut hide_rows = vec![];
|
for (index, block) in task_context.blocks.into_iter().enumerate() {
|
||||||
for block in task_context.blocks {
|
let row_ids = block
|
||||||
block.row_revs.iter().for_each(|row_rev| {
|
.row_revs
|
||||||
let result = filter_row(row_rev, &self.filter_cache, &self.filter_result_cache, &field_revs);
|
.par_iter()
|
||||||
|
.flat_map(|row_rev| {
|
||||||
|
let filter_result_cache = self.filter_result_cache.clone();
|
||||||
|
let filter_cache = self.filter_cache.clone();
|
||||||
|
filter_row(index, row_rev, filter_cache, filter_result_cache, &field_revs)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
if result.is_row_hidden() {
|
let mut visible_rows = vec![];
|
||||||
hide_rows.push(result.row_id);
|
let mut hide_rows = vec![];
|
||||||
|
for row_id in row_ids {
|
||||||
|
if self
|
||||||
|
.filter_result_cache
|
||||||
|
.get(&row_id)
|
||||||
|
.map(|result| result.is_visible())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
visible_rows.push(row_id);
|
||||||
} else {
|
} else {
|
||||||
show_rows.push(result.row_id);
|
hide_rows.push(row_id);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
self.notify(hide_rows, show_rows).await;
|
|
||||||
|
let changeset = GridBlockChangeset {
|
||||||
|
block_id: block.block_id,
|
||||||
|
hide_rows,
|
||||||
|
visible_rows,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
changesets.push(changeset);
|
||||||
|
}
|
||||||
|
self.notify(changesets).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,18 +106,17 @@ impl GridFilterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(filter_id) = &changeset.insert_filter {
|
if let Some(filter_id) = &changeset.insert_filter {
|
||||||
let mut cache = self.filter_cache.write().await;
|
|
||||||
let field_ids = Some(vec![filter_id.field_id.clone()]);
|
let field_ids = Some(vec![filter_id.field_id.clone()]);
|
||||||
reload_filter_cache(&mut cache, field_ids, &self.grid_pad).await;
|
reload_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(filter_id) = &changeset.delete_filter {
|
if let Some(filter_id) = &changeset.delete_filter {
|
||||||
self.filter_cache.write().await.remove(filter_id);
|
self.filter_cache.remove(filter_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(blocks) = self.block_manager.get_block_snapshots(None).await {
|
if let Ok(blocks) = self.block_manager.get_block_snapshots(None).await {
|
||||||
let task = self.gen_task(blocks).await;
|
let _task = self.gen_task(blocks).await;
|
||||||
let _ = self.scheduler.register_task(task).await;
|
// let _ = self.scheduler.register_task(task).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,25 +132,125 @@ impl GridFilterService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn notify(&self, _hide_rows: Vec<String>, _show_rows: Vec<String>) {
|
async fn notify(&self, changesets: Vec<GridBlockChangeset>) {
|
||||||
// let notification = GridNotification {};
|
for changeset in changesets {
|
||||||
// send_dart_notification(grid_id, GridNotification::DidUpdateGridBlock)
|
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridBlock)
|
||||||
// .payload(notification)
|
.payload(changeset)
|
||||||
// .send();
|
.send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return None if there is no change in this row after applying the filter
|
||||||
fn filter_row(
|
fn filter_row(
|
||||||
|
index: usize,
|
||||||
row_rev: &Arc<RowRevision>,
|
row_rev: &Arc<RowRevision>,
|
||||||
_filter_cache: &Arc<RwLock<FilterCache>>,
|
filter_cache: Arc<FilterCache>,
|
||||||
_filter_result_cache: &Arc<RwLock<FilterResultCache>>,
|
filter_result_cache: Arc<FilterResultCache>,
|
||||||
_field_revs: &HashMap<FieldId, Arc<FieldRevision>>,
|
field_revs: &HashMap<FieldId, Arc<FieldRevision>>,
|
||||||
) -> FilterResult {
|
) -> Option<String> {
|
||||||
let filter_result = FilterResult::new(row_rev);
|
let mut result = filter_result_cache
|
||||||
row_rev.cells.iter().for_each(|(_k, cell_rev)| {
|
.entry(row_rev.id.clone())
|
||||||
let _cell_rev: &CellRevision = cell_rev;
|
.or_insert(FilterResult::new(index as i32, row_rev));
|
||||||
});
|
|
||||||
filter_result
|
for (field_id, cell_rev) in row_rev.cells.iter() {
|
||||||
|
match filter_cell(field_revs, result.value_mut(), &filter_cache, field_id, cell_rev) {
|
||||||
|
None => {}
|
||||||
|
Some(_) => {
|
||||||
|
return Some(row_rev.id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return None if there is no change in this cell after applying the filter
|
||||||
|
fn filter_cell(
|
||||||
|
field_revs: &HashMap<FieldId, Arc<FieldRevision>>,
|
||||||
|
filter_result: &mut FilterResult,
|
||||||
|
filter_cache: &Arc<FilterCache>,
|
||||||
|
field_id: &str,
|
||||||
|
cell_rev: &CellRevision,
|
||||||
|
) -> Option<()> {
|
||||||
|
let field_rev = field_revs.get(field_id)?;
|
||||||
|
let field_type = FieldType::from(field_rev.field_type_rev);
|
||||||
|
let field_type_rev = field_type.clone().into();
|
||||||
|
let filter_id = FilterId {
|
||||||
|
field_id: field_id.to_owned(),
|
||||||
|
field_type,
|
||||||
|
};
|
||||||
|
let any_cell_data = AnyCellData::try_from(cell_rev).ok()?;
|
||||||
|
let is_hidden = match &filter_id.field_type {
|
||||||
|
FieldType::RichText => filter_cache.text_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<RichTextTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data.into(), filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FieldType::Number => filter_cache.number_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<NumberTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data, filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FieldType::DateTime => filter_cache.date_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<DateTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data, filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FieldType::SingleSelect => filter_cache.select_option_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<SingleSelectTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data.into(), filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FieldType::MultiSelect => filter_cache.select_option_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<MultiSelectTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data.into(), filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FieldType::Checkbox => filter_cache.checkbox_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<CheckboxTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data.into(), filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FieldType::URL => filter_cache.url_filter.get(&filter_id).and_then(|filter| {
|
||||||
|
Some(
|
||||||
|
field_rev
|
||||||
|
.get_type_option_entry::<URLTypeOption>(field_type_rev)?
|
||||||
|
.apply_filter(any_cell_data.into(), filter.value()),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let is_visible = !is_hidden;
|
||||||
|
match filter_result.visible_by_field_id.get(&filter_id) {
|
||||||
|
None => {
|
||||||
|
if is_visible {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
filter_result.visible_by_field_id.insert(filter_id, is_visible);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(old_is_visible) => {
|
||||||
|
if old_is_visible != &is_visible {
|
||||||
|
filter_result.visible_by_field_id.insert(filter_id, is_visible);
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridFilterChangeset {
|
pub struct GridFilterChangeset {
|
||||||
@ -150,144 +281,3 @@ impl std::convert::From<&GridSettingChangesetParams> for GridFilterChangeset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct FilterResultCache {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
rows: HashMap<String, FilterResult>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterResultCache {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn insert(&mut self, row_id: &str, result: FilterResult) {
|
|
||||||
self.rows.insert(row_id.to_owned(), result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct FilterResult {
|
|
||||||
row_id: String,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
cell_by_field_id: HashMap<String, bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterResult {
|
|
||||||
fn new(row_rev: &RowRevision) -> Self {
|
|
||||||
Self {
|
|
||||||
row_id: row_rev.id.clone(),
|
|
||||||
cell_by_field_id: row_rev.cells.iter().map(|(k, _)| (k.clone(), true)).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn update_cell(&mut self, cell_id: &str, exist: bool) {
|
|
||||||
self.cell_by_field_id.insert(cell_id.to_owned(), exist);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_row_hidden(&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct FilterCache {
|
|
||||||
text_filter: HashMap<FilterId, GridTextFilter>,
|
|
||||||
url_filter: HashMap<FilterId, GridTextFilter>,
|
|
||||||
number_filter: HashMap<FilterId, GridNumberFilter>,
|
|
||||||
date_filter: HashMap<FilterId, GridDateFilter>,
|
|
||||||
select_option_filter: HashMap<FilterId, GridSelectOptionFilter>,
|
|
||||||
checkbox_filter: HashMap<FilterId, GridCheckboxFilter>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterCache {
|
|
||||||
async fn from_grid_pad(grid_pad: &Arc<RwLock<GridRevisionPad>>) -> Self {
|
|
||||||
let mut this = Self::default();
|
|
||||||
let _ = reload_filter_cache(&mut this, None, grid_pad).await;
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove(&mut self, filter_id: &FilterId) {
|
|
||||||
let _ = match filter_id.field_type {
|
|
||||||
FieldType::RichText => {
|
|
||||||
let _ = self.text_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
FieldType::Number => {
|
|
||||||
let _ = self.number_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
FieldType::DateTime => {
|
|
||||||
let _ = self.date_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
FieldType::SingleSelect => {
|
|
||||||
let _ = self.select_option_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
FieldType::MultiSelect => {
|
|
||||||
let _ = self.select_option_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
FieldType::Checkbox => {
|
|
||||||
let _ = self.checkbox_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
FieldType::URL => {
|
|
||||||
let _ = self.url_filter.remove(filter_id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn reload_filter_cache(
|
|
||||||
cache: &mut FilterCache,
|
|
||||||
field_ids: Option<Vec<String>>,
|
|
||||||
grid_pad: &Arc<RwLock<GridRevisionPad>>,
|
|
||||||
) {
|
|
||||||
let grid_pad = grid_pad.read().await;
|
|
||||||
let filters_revs = grid_pad.get_filters(None, field_ids).unwrap_or_default();
|
|
||||||
|
|
||||||
for filter_rev in filters_revs {
|
|
||||||
match grid_pad.get_field_rev(&filter_rev.field_id) {
|
|
||||||
None => {}
|
|
||||||
Some((_, field_rev)) => {
|
|
||||||
let filter_id = FilterId::from(field_rev);
|
|
||||||
let field_type: FieldType = field_rev.field_type_rev.into();
|
|
||||||
match &field_type {
|
|
||||||
FieldType::RichText => {
|
|
||||||
let _ = cache.text_filter.insert(filter_id, GridTextFilter::from(filter_rev));
|
|
||||||
}
|
|
||||||
FieldType::Number => {
|
|
||||||
let _ = cache
|
|
||||||
.number_filter
|
|
||||||
.insert(filter_id, GridNumberFilter::from(filter_rev));
|
|
||||||
}
|
|
||||||
FieldType::DateTime => {
|
|
||||||
let _ = cache.date_filter.insert(filter_id, GridDateFilter::from(filter_rev));
|
|
||||||
}
|
|
||||||
FieldType::SingleSelect | FieldType::MultiSelect => {
|
|
||||||
let _ = cache
|
|
||||||
.select_option_filter
|
|
||||||
.insert(filter_id, GridSelectOptionFilter::from(filter_rev));
|
|
||||||
}
|
|
||||||
FieldType::Checkbox => {
|
|
||||||
let _ = cache
|
|
||||||
.checkbox_filter
|
|
||||||
.insert(filter_id, GridCheckboxFilter::from(filter_rev));
|
|
||||||
}
|
|
||||||
FieldType::URL => {
|
|
||||||
let _ = cache.url_filter.insert(filter_id, GridTextFilter::from(filter_rev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Hash, Eq, PartialEq)]
|
|
||||||
struct FilterId {
|
|
||||||
field_id: String,
|
|
||||||
field_type: FieldType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<&Arc<FieldRevision>> for FilterId {
|
|
||||||
fn from(rev: &Arc<FieldRevision>) -> Self {
|
|
||||||
Self {
|
|
||||||
field_id: rev.id.clone(),
|
|
||||||
field_type: rev.field_type_rev.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
mod filter_cache;
|
||||||
mod filter_service;
|
mod filter_service;
|
||||||
|
|
||||||
pub(crate) use filter_service::*;
|
pub(crate) use filter_service::*;
|
||||||
|
@ -265,14 +265,14 @@ impl GridRevisionEditor {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<BlockRowInfo> {
|
pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowInfo> {
|
||||||
let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
|
let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
|
||||||
let block_id = self.block_id().await?;
|
let block_id = self.block_id().await?;
|
||||||
|
|
||||||
// insert empty row below the row whose id is upper_row_id
|
// insert empty row below the row whose id is upper_row_id
|
||||||
let row_rev_ctx = CreateRowRevisionBuilder::new(&field_revs).build();
|
let row_rev_ctx = CreateRowRevisionBuilder::new(&field_revs).build();
|
||||||
let row_rev = make_row_rev_from_context(&block_id, row_rev_ctx);
|
let row_rev = make_row_rev_from_context(&block_id, row_rev_ctx);
|
||||||
let row_order = BlockRowInfo::from(&row_rev);
|
let row_order = RowInfo::from(&row_rev);
|
||||||
|
|
||||||
// insert the row
|
// insert the row
|
||||||
let row_count = self.block_manager.create_row(&block_id, row_rev, start_row_id).await?;
|
let row_count = self.block_manager.create_row(&block_id, row_rev, start_row_id).await?;
|
||||||
@ -283,13 +283,13 @@ impl GridRevisionEditor {
|
|||||||
Ok(row_order)
|
Ok(row_order)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert_rows(&self, contexts: Vec<CreateRowRevisionPayload>) -> FlowyResult<Vec<BlockRowInfo>> {
|
pub async fn insert_rows(&self, contexts: Vec<CreateRowRevisionPayload>) -> FlowyResult<Vec<RowInfo>> {
|
||||||
let block_id = self.block_id().await?;
|
let block_id = self.block_id().await?;
|
||||||
let mut rows_by_block_id: HashMap<String, Vec<RowRevision>> = HashMap::new();
|
let mut rows_by_block_id: HashMap<String, Vec<RowRevision>> = HashMap::new();
|
||||||
let mut row_orders = vec![];
|
let mut row_orders = vec![];
|
||||||
for ctx in contexts {
|
for ctx in contexts {
|
||||||
let row_rev = make_row_rev_from_context(&block_id, ctx);
|
let row_rev = make_row_rev_from_context(&block_id, ctx);
|
||||||
row_orders.push(BlockRowInfo::from(&row_rev));
|
row_orders.push(RowInfo::from(&row_rev));
|
||||||
rows_by_block_id
|
rows_by_block_id
|
||||||
.entry(block_id.clone())
|
.entry(block_id.clone())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
@ -352,7 +352,7 @@ impl GridRevisionEditor {
|
|||||||
let row_rev = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??;
|
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();
|
let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone();
|
||||||
let data = decode_cell_data(cell_rev.data, &field_rev).data;
|
let data = decode_any_cell_data(cell_rev.data, &field_rev).data;
|
||||||
Some(Cell::new(¶ms.field_id, data))
|
Some(Cell::new(¶ms.field_id, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +421,7 @@ impl GridRevisionEditor {
|
|||||||
Ok(block_meta_revs)
|
Ok(block_meta_revs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_rows(&self, row_orders: Vec<BlockRowInfo>) -> FlowyResult<()> {
|
pub async fn delete_rows(&self, row_orders: Vec<RowInfo>) -> FlowyResult<()> {
|
||||||
let changesets = self.block_manager.delete_rows(row_orders).await?;
|
let changesets = self.block_manager.delete_rows(row_orders).await?;
|
||||||
for changeset in changesets {
|
for changeset in changesets {
|
||||||
let _ = self.update_block(changeset).await?;
|
let _ = self.update_block(changeset).await?;
|
||||||
|
@ -2,23 +2,25 @@ use crate::entities::FieldType;
|
|||||||
use crate::services::field::*;
|
use crate::services::field::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
||||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision};
|
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, FieldTypeRevision};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub trait CellDataOperation<D, F> {
|
pub trait CellFilterOperation<T, C: From<AnyCellData>> {
|
||||||
|
fn apply_filter(&self, cell_data: C, filter: &T) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CellDataOperation<D> {
|
||||||
fn decode_cell_data<T>(
|
fn decode_cell_data<T>(
|
||||||
&self,
|
&self,
|
||||||
encoded_data: T,
|
cell_data: T,
|
||||||
decoded_field_type: &FieldType,
|
decoded_field_type: &FieldType,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<DecodedCellData>
|
) -> FlowyResult<DecodedCellData>
|
||||||
where
|
where
|
||||||
T: Into<D>;
|
T: Into<D>;
|
||||||
|
|
||||||
fn apply_filter(&self, filter: F) -> bool;
|
|
||||||
|
|
||||||
fn apply_changeset<C: Into<CellContentChangeset>>(
|
fn apply_changeset<C: Into<CellContentChangeset>>(
|
||||||
&self,
|
&self,
|
||||||
changeset: C,
|
changeset: C,
|
||||||
@ -51,32 +53,40 @@ impl std::ops::Deref for CellContentChangeset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct TypeOptionCellData {
|
pub struct AnyCellData {
|
||||||
pub data: String,
|
pub cell_data: String,
|
||||||
pub field_type: FieldType,
|
pub field_type: FieldType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for TypeOptionCellData {
|
impl std::str::FromStr for AnyCellData {
|
||||||
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 type_option_cell_data: TypeOptionCellData = serde_json::from_str(s)?;
|
let type_option_cell_data: AnyCellData = serde_json::from_str(s)?;
|
||||||
Ok(type_option_cell_data)
|
Ok(type_option_cell_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::TryInto<TypeOptionCellData> for String {
|
impl std::convert::TryInto<AnyCellData> for String {
|
||||||
type Error = FlowyError;
|
type Error = FlowyError;
|
||||||
|
|
||||||
fn try_into(self) -> Result<TypeOptionCellData, Self::Error> {
|
fn try_into(self) -> Result<AnyCellData, Self::Error> {
|
||||||
TypeOptionCellData::from_str(&self)
|
AnyCellData::from_str(&self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeOptionCellData {
|
impl std::convert::TryFrom<&CellRevision> for AnyCellData {
|
||||||
pub fn new<T: ToString>(data: T, field_type: FieldType) -> Self {
|
type Error = FlowyError;
|
||||||
TypeOptionCellData {
|
|
||||||
data: data.to_string(),
|
fn try_from(value: &CellRevision) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_str(&value.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyCellData {
|
||||||
|
pub fn new(content: String, field_type: FieldType) -> Self {
|
||||||
|
AnyCellData {
|
||||||
|
cell_data: content,
|
||||||
field_type,
|
field_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,14 +143,14 @@ pub fn apply_cell_data_changeset<C: Into<CellContentChangeset>, T: AsRef<FieldRe
|
|||||||
FieldType::URL => URLTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
|
FieldType::URL => URLTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Ok(TypeOptionCellData::new(s, field_type).json())
|
Ok(AnyCellData::new(s, field_type).json())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_cell_data<T: TryInto<TypeOptionCellData>>(data: T, field_rev: &FieldRevision) -> DecodedCellData {
|
pub fn decode_any_cell_data<T: TryInto<AnyCellData>>(data: T, field_rev: &FieldRevision) -> DecodedCellData {
|
||||||
if let Ok(type_option_cell_data) = data.try_into() {
|
if let Ok(any_cell_data) = data.try_into() {
|
||||||
let TypeOptionCellData { data, field_type } = type_option_cell_data;
|
let AnyCellData { cell_data, field_type } = any_cell_data;
|
||||||
let to_field_type = field_rev.field_type_rev.into();
|
let to_field_type = field_rev.field_type_rev.into();
|
||||||
match try_decode_cell_data(data, field_rev, &field_type, &to_field_type) {
|
match try_decode_cell_data(cell_data, field_rev, &field_type, &to_field_type) {
|
||||||
Ok(cell_data) => cell_data,
|
Ok(cell_data) => cell_data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Decode cell data failed, {:?}", e);
|
tracing::error!("Decode cell data failed, {:?}", e);
|
||||||
@ -154,34 +164,35 @@ pub fn decode_cell_data<T: TryInto<TypeOptionCellData>>(data: T, field_rev: &Fie
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_decode_cell_data(
|
pub fn try_decode_cell_data(
|
||||||
encoded_data: String,
|
cell_data: String,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
s_field_type: &FieldType,
|
s_field_type: &FieldType,
|
||||||
t_field_type: &FieldType,
|
t_field_type: &FieldType,
|
||||||
) -> FlowyResult<DecodedCellData> {
|
) -> FlowyResult<DecodedCellData> {
|
||||||
let get_cell_data = || {
|
let get_cell_data = || {
|
||||||
|
let field_type: FieldTypeRevision = t_field_type.into();
|
||||||
let data = match t_field_type {
|
let data = match t_field_type {
|
||||||
FieldType::RichText => field_rev
|
FieldType::RichText => field_rev
|
||||||
.get_type_option_entry::<RichTextTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<RichTextTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
FieldType::Number => field_rev
|
FieldType::Number => field_rev
|
||||||
.get_type_option_entry::<NumberTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<NumberTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
FieldType::DateTime => field_rev
|
FieldType::DateTime => field_rev
|
||||||
.get_type_option_entry::<DateTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<DateTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
FieldType::SingleSelect => field_rev
|
FieldType::SingleSelect => field_rev
|
||||||
.get_type_option_entry::<SingleSelectTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<SingleSelectTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
FieldType::MultiSelect => field_rev
|
FieldType::MultiSelect => field_rev
|
||||||
.get_type_option_entry::<MultiSelectTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<MultiSelectTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
FieldType::Checkbox => field_rev
|
FieldType::Checkbox => field_rev
|
||||||
.get_type_option_entry::<CheckboxTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<CheckboxTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
FieldType::URL => field_rev
|
FieldType::URL => field_rev
|
||||||
.get_type_option_entry::<URLTypeOption, _>(t_field_type)?
|
.get_type_option_entry::<URLTypeOption>(field_type)?
|
||||||
.decode_cell_data(encoded_data, s_field_type, field_rev),
|
.decode_cell_data(cell_data, s_field_type, field_rev),
|
||||||
};
|
};
|
||||||
Some(data)
|
Some(data)
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::entities::{BlockRowInfo, GridBlock, RepeatedGridBlock, Row};
|
use crate::entities::{GridBlock, RepeatedGridBlock, Row, RowInfo};
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -9,7 +9,7 @@ pub struct GridBlockSnapshot {
|
|||||||
pub row_revs: Vec<Arc<RowRevision>>,
|
pub row_revs: Vec<Arc<RowRevision>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn block_from_row_orders(row_orders: Vec<BlockRowInfo>) -> Vec<GridBlock> {
|
pub(crate) fn block_from_row_orders(row_orders: Vec<RowInfo>) -> Vec<GridBlock> {
|
||||||
let mut map: HashMap<String, GridBlock> = HashMap::new();
|
let mut map: HashMap<String, GridBlock> = HashMap::new();
|
||||||
row_orders.into_iter().for_each(|row_info| {
|
row_orders.into_iter().for_each(|row_info| {
|
||||||
// Memory Optimization: escape clone block_id
|
// Memory Optimization: escape clone block_id
|
||||||
@ -35,8 +35,8 @@ pub(crate) fn block_from_row_orders(row_orders: Vec<BlockRowInfo>) -> Vec<GridBl
|
|||||||
// Some((field_id, cell))
|
// Some((field_id, cell))
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub(crate) fn make_row_orders_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<BlockRowInfo> {
|
pub(crate) fn make_row_orders_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<RowInfo> {
|
||||||
row_revs.iter().map(BlockRowInfo::from).collect::<Vec<_>>()
|
row_revs.iter().map(RowInfo::from).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn make_row_from_row_rev(fields: &[Arc<FieldRevision>], row_rev: Arc<RowRevision>) -> Option<Row> {
|
pub(crate) fn make_row_from_row_rev(fields: &[Arc<FieldRevision>], row_rev: Arc<RowRevision>) -> Option<Row> {
|
||||||
|
@ -12,7 +12,7 @@ pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
|
|||||||
let cloned_field_rev = field_rev.clone();
|
let cloned_field_rev = field_rev.clone();
|
||||||
|
|
||||||
let type_option_data = field_rev
|
let type_option_data = field_rev
|
||||||
.get_type_option_entry::<RichTextTypeOption, _>(field_rev.field_type_rev)
|
.get_type_option_entry::<RichTextTypeOption>(field_rev.field_type_rev)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.protobuf_bytes()
|
.protobuf_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
@ -44,9 +44,8 @@ pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRev
|
|||||||
|
|
||||||
let field_rev = FieldBuilder::new(single_select).name("Name").visibility(true).build();
|
let field_rev = FieldBuilder::new(single_select).name("Name").visibility(true).build();
|
||||||
let cloned_field_rev = field_rev.clone();
|
let cloned_field_rev = field_rev.clone();
|
||||||
let field_type: FieldType = field_rev.field_type_rev.into();
|
|
||||||
let type_option_data = field_rev
|
let type_option_data = field_rev
|
||||||
.get_type_option_entry::<SingleSelectTypeOption, _>(&field_type)
|
.get_type_option_entry::<SingleSelectTypeOption>(field_rev.field_type_rev)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.protobuf_bytes()
|
.protobuf_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
@ -55,7 +54,7 @@ pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRev
|
|||||||
id: field_rev.id,
|
id: field_rev.id,
|
||||||
name: field_rev.name,
|
name: field_rev.name,
|
||||||
desc: field_rev.desc,
|
desc: field_rev.desc,
|
||||||
field_type,
|
field_type: field_rev.field_type_rev.into(),
|
||||||
frozen: field_rev.frozen,
|
frozen: field_rev.frozen,
|
||||||
visibility: field_rev.visibility,
|
visibility: field_rev.visibility,
|
||||||
width: field_rev.width,
|
width: field_rev.width,
|
||||||
|
@ -7,7 +7,7 @@ use flowy_grid::entities::FieldType;
|
|||||||
use flowy_grid::services::field::{
|
use flowy_grid::services::field::{
|
||||||
DateCellData, MultiSelectTypeOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
|
DateCellData, MultiSelectTypeOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
|
||||||
};
|
};
|
||||||
use flowy_grid::services::row::{decode_cell_data, CreateRowRevisionBuilder};
|
use flowy_grid::services::row::{decode_any_cell_data, CreateRowRevisionBuilder};
|
||||||
use flowy_grid_data_model::revision::RowMetaChangeset;
|
use flowy_grid_data_model::revision::RowMetaChangeset;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -137,9 +137,9 @@ async fn grid_row_add_date_cell_test() {
|
|||||||
}
|
}
|
||||||
let context = builder.build();
|
let context = builder.build();
|
||||||
let date_field = date_field.unwrap();
|
let date_field = date_field.unwrap();
|
||||||
let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
|
let cell_rev = context.cell_by_field_id.get(&date_field.id).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
decode_cell_data(cell_data.data.clone(), &date_field)
|
decode_any_cell_data(cell_rev, &date_field)
|
||||||
.parse::<DateCellData>()
|
.parse::<DateCellData>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.date,
|
.date,
|
||||||
|
@ -99,7 +99,7 @@ pub struct GridEditorTest {
|
|||||||
pub row_revs: Vec<Arc<RowRevision>>,
|
pub row_revs: Vec<Arc<RowRevision>>,
|
||||||
pub field_count: usize,
|
pub field_count: usize,
|
||||||
|
|
||||||
pub row_order_by_row_id: HashMap<String, BlockRowInfo>,
|
pub row_order_by_row_id: HashMap<String, RowInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridEditorTest {
|
impl GridEditorTest {
|
||||||
@ -220,7 +220,7 @@ impl GridEditorTest {
|
|||||||
let row_orders = row_ids
|
let row_orders = row_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
|
.map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
|
||||||
.collect::<Vec<BlockRowInfo>>();
|
.collect::<Vec<RowInfo>>();
|
||||||
|
|
||||||
self.editor.delete_rows(row_orders).await.unwrap();
|
self.editor.delete_rows(row_orders).await.unwrap();
|
||||||
self.row_revs = self.get_row_revs().await;
|
self.row_revs = self.get_row_revs().await;
|
||||||
|
@ -168,13 +168,9 @@ impl FieldRevision {
|
|||||||
self.type_options.insert(id, entry.json_str());
|
self.type_options.insert(id, entry.json_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_type_option_entry<T1: TypeOptionDataDeserializer, T2: Into<FieldTypeRevision>>(
|
pub fn get_type_option_entry<T: TypeOptionDataDeserializer>(&self, field_type_rev: FieldTypeRevision) -> Option<T> {
|
||||||
&self,
|
|
||||||
field_type: T2,
|
|
||||||
) -> Option<T1> {
|
|
||||||
let field_type_rev = field_type.into();
|
|
||||||
let id = field_type_rev.to_string();
|
let id = field_type_rev.to_string();
|
||||||
self.type_options.get(&id).map(|s| T1::from_json_str(s))
|
self.type_options.get(&id).map(|s| T::from_json_str(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_type_option_str(&mut self, field_type: &FieldTypeRevision, json_str: String) {
|
pub fn insert_type_option_str(&mut self, field_type: &FieldTypeRevision, json_str: String) {
|
||||||
|
Loading…
Reference in New Issue
Block a user