mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Feat/sort after change (#1607)
* chore: generate task after row was changed * chore: config task * chore: add task test Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
5a30f46b85
commit
f5b7d3951f
@ -17,6 +17,8 @@ pub enum GridDartNotification {
|
||||
DidGroupByNewField = 62,
|
||||
DidUpdateFilter = 63,
|
||||
DidUpdateSort = 64,
|
||||
DidReorderRows = 65,
|
||||
DidReorderSingleRow = 66,
|
||||
DidUpdateGridSetting = 70,
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::services::sort::SortType;
|
||||
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
use grid_rev_model::FieldTypeRevision;
|
||||
use grid_rev_model::{FieldTypeRevision, SortCondition, SortRevision};
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct GridSortPB {
|
||||
@ -21,6 +21,17 @@ pub struct GridSortPB {
|
||||
pub condition: GridSortConditionPB,
|
||||
}
|
||||
|
||||
impl std::convert::From<&SortRevision> for GridSortPB {
|
||||
fn from(sort_rev: &SortRevision) -> Self {
|
||||
Self {
|
||||
id: sort_rev.id.clone(),
|
||||
field_id: sort_rev.field_id.clone(),
|
||||
field_type: sort_rev.field_type.into(),
|
||||
condition: sort_rev.condition.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum GridSortConditionPB {
|
||||
@ -32,6 +43,16 @@ impl std::default::Default for GridSortConditionPB {
|
||||
Self::Ascending
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<SortCondition> for GridSortConditionPB {
|
||||
fn from(condition: SortCondition) -> Self {
|
||||
match condition {
|
||||
SortCondition::Ascending => GridSortConditionPB::Ascending,
|
||||
SortCondition::Descending => GridSortConditionPB::Descending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct AlterSortPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
@ -151,3 +172,30 @@ pub struct SortChangesetNotificationPB {
|
||||
#[pb(index = 4)]
|
||||
pub update_sorts: Vec<GridSortPB>,
|
||||
}
|
||||
|
||||
impl SortChangesetNotificationPB {
|
||||
pub fn extend(&mut self, other: SortChangesetNotificationPB) {
|
||||
self.insert_sorts.extend(other.insert_sorts);
|
||||
self.delete_sorts.extend(other.delete_sorts);
|
||||
self.update_sorts.extend(other.update_sorts);
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.insert_sorts.is_empty() && self.delete_sorts.is_empty() && self.update_sorts.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct ReorderAllRowsPB {
|
||||
#[pb(index = 1)]
|
||||
pub row_orders: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct ReorderSingleRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub old_index: i32,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub new_index: i32,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::*;
|
||||
use crate::manager::GridManager;
|
||||
use crate::services::cell::{FromCellString, TypeCellData};
|
||||
use crate::services::cell::{FromCellString, ToCellChangesetString, TypeCellData};
|
||||
use crate::services::field::{
|
||||
default_type_option_builder_from_type, select_type_option_from_field_rev, type_option_builder_from_json_str,
|
||||
DateCellChangeset, DateChangesetPB, SelectOptionCellChangeset, SelectOptionCellChangesetPB,
|
||||
@ -317,7 +317,9 @@ pub(crate) async fn update_cell_handler(
|
||||
) -> Result<(), FlowyError> {
|
||||
let changeset: CellChangesetPB = data.into_inner();
|
||||
let editor = manager.get_grid_editor(&changeset.grid_id).await?;
|
||||
let _ = editor.update_cell_with_changeset(changeset).await?;
|
||||
let _ = editor
|
||||
.update_cell_with_changeset(&changeset.row_id, &changeset.field_id, changeset.type_cell_data)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -344,16 +346,17 @@ pub(crate) async fn update_select_option_handler(
|
||||
manager: AFPluginState<Arc<GridManager>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let changeset: SelectOptionChangeset = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(&changeset.cell_identifier.view_id).await?;
|
||||
|
||||
let editor = manager.get_grid_editor(&changeset.cell_path.view_id).await?;
|
||||
let field_id = changeset.cell_path.field_id.clone();
|
||||
let _ = editor
|
||||
.modify_field_rev(&changeset.cell_identifier.field_id, |field_rev| {
|
||||
.modify_field_rev(&field_id, |field_rev| {
|
||||
let mut type_option = select_type_option_from_field_rev(field_rev)?;
|
||||
let mut cell_content_changeset = None;
|
||||
let mut cell_changeset_str = None;
|
||||
let mut is_changed = None;
|
||||
|
||||
for option in changeset.insert_options {
|
||||
cell_content_changeset = Some(SelectOptionCellChangeset::from_insert_option_id(&option.id).to_str());
|
||||
cell_changeset_str =
|
||||
Some(SelectOptionCellChangeset::from_insert_option_id(&option.id).to_cell_changeset_str());
|
||||
type_option.insert_option(option);
|
||||
is_changed = Some(());
|
||||
}
|
||||
@ -364,7 +367,8 @@ pub(crate) async fn update_select_option_handler(
|
||||
}
|
||||
|
||||
for option in changeset.delete_options {
|
||||
cell_content_changeset = Some(SelectOptionCellChangeset::from_delete_option_id(&option.id).to_str());
|
||||
cell_changeset_str =
|
||||
Some(SelectOptionCellChangeset::from_delete_option_id(&option.id).to_cell_changeset_str());
|
||||
type_option.delete_option(option);
|
||||
is_changed = Some(());
|
||||
}
|
||||
@ -373,16 +377,17 @@ pub(crate) async fn update_select_option_handler(
|
||||
field_rev.insert_type_option(&*type_option);
|
||||
}
|
||||
|
||||
if let Some(cell_content_changeset) = cell_content_changeset {
|
||||
let changeset = CellChangesetPB {
|
||||
grid_id: changeset.cell_identifier.view_id,
|
||||
row_id: changeset.cell_identifier.row_id,
|
||||
field_id: changeset.cell_identifier.field_id.clone(),
|
||||
type_cell_data: cell_content_changeset,
|
||||
};
|
||||
if let Some(cell_changeset_str) = cell_changeset_str {
|
||||
let cloned_editor = editor.clone();
|
||||
tokio::spawn(async move {
|
||||
match cloned_editor.update_cell_with_changeset(changeset).await {
|
||||
match cloned_editor
|
||||
.update_cell_with_changeset(
|
||||
&changeset.cell_path.row_id,
|
||||
&changeset.cell_path.field_id,
|
||||
cell_changeset_str,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => tracing::error!("{}", e),
|
||||
}
|
||||
@ -432,7 +437,18 @@ pub(crate) async fn update_select_option_cell_handler(
|
||||
) -> Result<(), FlowyError> {
|
||||
let params: SelectOptionCellChangesetParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(¶ms.cell_identifier.view_id).await?;
|
||||
let _ = editor.update_cell_with_changeset(params.into()).await?;
|
||||
let changeset = SelectOptionCellChangeset {
|
||||
insert_option_ids: params.insert_option_ids,
|
||||
delete_option_ids: params.delete_option_ids,
|
||||
};
|
||||
|
||||
let _ = editor
|
||||
.update_cell_with_changeset(
|
||||
¶ms.cell_identifier.row_id,
|
||||
¶ms.cell_identifier.field_id,
|
||||
changeset,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -443,7 +459,7 @@ pub(crate) async fn update_date_cell_handler(
|
||||
) -> Result<(), FlowyError> {
|
||||
let data = data.into_inner();
|
||||
let cell_path: CellPathParams = data.cell_path.try_into()?;
|
||||
let content = DateCellChangeset {
|
||||
let cell_changeset = DateCellChangeset {
|
||||
date: data.date,
|
||||
time: data.time,
|
||||
is_utc: data.is_utc,
|
||||
@ -451,7 +467,7 @@ pub(crate) async fn update_date_cell_handler(
|
||||
|
||||
let editor = manager.get_grid_editor(&cell_path.view_id).await?;
|
||||
let _ = editor
|
||||
.update_cell(cell_path.view_id, cell_path.row_id, cell_path.field_id, content)
|
||||
.update_cell(cell_path.row_id, cell_path.field_id, cell_changeset)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -118,8 +118,13 @@ impl GridBlockRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<(usize, Arc<RowRevision>)>> {
|
||||
let row_rev = self.pad.read().await.get_row_rev(row_id);
|
||||
Ok(row_rev)
|
||||
if self.pad.try_read().is_err() {
|
||||
tracing::error!("Required GridBlockRevisionPad's read lock failed");
|
||||
Ok(None)
|
||||
} else {
|
||||
let row_rev = self.pad.read().await.get_row_rev(row_id);
|
||||
Ok(row_rev)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_row_revs<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<Arc<RowRevision>>>
|
||||
|
@ -34,7 +34,7 @@ pub trait CellDataDecoder: TypeOption {
|
||||
|
||||
pub trait CellDataChangeset: TypeOption {
|
||||
/// The changeset is able to parse into the concrete data struct if `TypeOption::CellChangeset`
|
||||
/// implements the `FromCellChangeset` trait.
|
||||
/// implements the `FromCellChangesetString` trait.
|
||||
/// For example,the SelectOptionCellChangeset,DateCellChangeset. etc.
|
||||
///
|
||||
fn apply_changeset(
|
||||
@ -50,14 +50,14 @@ pub trait CellDataChangeset: TypeOption {
|
||||
/// FieldType::SingleSelect => SelectOptionChangeset
|
||||
///
|
||||
/// cell_rev: It will be None if the cell does not contain any data.
|
||||
pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
|
||||
pub fn apply_cell_data_changeset<C: ToCellChangesetString, T: AsRef<FieldRevision>>(
|
||||
changeset: C,
|
||||
cell_rev: Option<CellRevision>,
|
||||
field_rev: T,
|
||||
cell_data_cache: Option<AtomicCellDataCache>,
|
||||
) -> Result<String, FlowyError> {
|
||||
let field_rev = field_rev.as_ref();
|
||||
let changeset = changeset.to_string();
|
||||
let changeset = changeset.to_cell_changeset_str();
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
|
||||
let type_cell_data = cell_rev.and_then(|cell_rev| match TypeCellData::try_from(cell_rev) {
|
||||
@ -108,7 +108,8 @@ pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debu
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cell_str`: the opaque cell string
|
||||
/// * `cell_str`: the opaque cell string that can be decoded by corresponding structs that implement the
|
||||
/// `FromCellString` trait.
|
||||
/// * `from_field_type`: the original field type of the passed-in cell data. Check the `TypeCellData`
|
||||
/// that is used to save the origin field type of the cell data.
|
||||
/// * `to_field_type`: decode the passed-in cell data to this field type. It will use the to_field_type's
|
||||
@ -155,7 +156,7 @@ pub fn insert_text_cell(s: String, field_rev: &FieldRevision) -> CellRevision {
|
||||
}
|
||||
|
||||
pub fn insert_number_cell(num: i64, field_rev: &FieldRevision) -> CellRevision {
|
||||
let data = apply_cell_data_changeset(num, None, field_rev, None).unwrap();
|
||||
let data = apply_cell_data_changeset(num.to_string(), None, field_rev, None).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
@ -186,14 +187,14 @@ pub fn insert_date_cell(timestamp: i64, field_rev: &FieldRevision) -> CellRevisi
|
||||
}
|
||||
|
||||
pub fn insert_select_option_cell(option_ids: Vec<String>, field_rev: &FieldRevision) -> CellRevision {
|
||||
let cell_data = SelectOptionCellChangeset::from_insert_options(option_ids).to_str();
|
||||
let data = apply_cell_data_changeset(cell_data, None, field_rev, None).unwrap();
|
||||
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids).to_cell_changeset_str();
|
||||
let data = apply_cell_data_changeset(changeset, None, field_rev, None).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
pub fn delete_select_option_cell(option_ids: Vec<String>, field_rev: &FieldRevision) -> CellRevision {
|
||||
let cell_data = SelectOptionCellChangeset::from_delete_options(option_ids).to_str();
|
||||
let data = apply_cell_data_changeset(cell_data, None, field_rev, None).unwrap();
|
||||
let changeset = SelectOptionCellChangeset::from_delete_options(option_ids).to_cell_changeset_str();
|
||||
let data = apply_cell_data_changeset(changeset, None, field_rev, None).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
@ -252,13 +253,13 @@ impl std::convert::From<IntoCellData<String>> for String {
|
||||
|
||||
/// If the changeset applying to the cell is not String type, it should impl this trait.
|
||||
/// Deserialize the string into cell specific changeset.
|
||||
pub trait FromCellChangeset {
|
||||
pub trait FromCellChangesetString {
|
||||
fn from_changeset(changeset: String) -> FlowyResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl FromCellChangeset for String {
|
||||
impl FromCellChangesetString for String {
|
||||
fn from_changeset(changeset: String) -> FlowyResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -267,6 +268,16 @@ impl FromCellChangeset for String {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToCellChangesetString: Debug {
|
||||
fn to_cell_changeset_str(&self) -> String;
|
||||
}
|
||||
|
||||
impl ToCellChangesetString for String {
|
||||
fn to_cell_changeset_str(&self) -> String {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyCellChangeset<T>(pub Option<T>);
|
||||
|
||||
impl<T> AnyCellChangeset<T> {
|
||||
@ -280,7 +291,7 @@ impl<T> AnyCellChangeset<T> {
|
||||
|
||||
impl<T, C: ToString> std::convert::From<C> for AnyCellChangeset<T>
|
||||
where
|
||||
T: FromCellChangeset,
|
||||
T: FromCellChangesetString,
|
||||
{
|
||||
fn from(changeset: C) -> Self {
|
||||
match T::from_changeset(changeset.to_string()) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::entities::{DateFilterConditionPB, DateFilterPB};
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
|
||||
impl DateFilterPB {
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::entities::CellPathPB;
|
||||
use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString};
|
||||
use crate::services::cell::{
|
||||
CellProtobufBlobParser, DecodedCellData, FromCellChangesetString, FromCellString, ToCellChangesetString,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::{internal_error, FlowyResult};
|
||||
@ -33,7 +35,7 @@ pub struct DateChangesetPB {
|
||||
pub is_utc: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DateCellChangeset {
|
||||
pub date: Option<String>,
|
||||
pub time: Option<String>,
|
||||
@ -53,7 +55,7 @@ impl DateCellChangeset {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromCellChangeset for DateCellChangeset {
|
||||
impl FromCellChangesetString for DateCellChangeset {
|
||||
fn from_changeset(changeset: String) -> FlowyResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -62,9 +64,9 @@ impl FromCellChangeset for DateCellChangeset {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for DateCellChangeset {
|
||||
fn to_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "".to_string())
|
||||
impl ToCellChangesetString for DateCellChangeset {
|
||||
fn to_cell_changeset_str(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::{CellChangesetPB, CellPathPB, CellPathParams, FieldType};
|
||||
use crate::entities::{CellPathPB, CellPathParams, FieldType};
|
||||
use crate::services::cell::{
|
||||
CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString,
|
||||
CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangesetString, FromCellString,
|
||||
ToCellChangesetString,
|
||||
};
|
||||
|
||||
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
|
||||
@ -379,22 +380,6 @@ pub struct SelectOptionCellChangesetParams {
|
||||
pub delete_option_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl std::convert::From<SelectOptionCellChangesetParams> for CellChangesetPB {
|
||||
fn from(params: SelectOptionCellChangesetParams) -> Self {
|
||||
let changeset = SelectOptionCellChangeset {
|
||||
insert_option_ids: params.insert_option_ids,
|
||||
delete_option_ids: params.delete_option_ids,
|
||||
};
|
||||
let content = serde_json::to_string(&changeset).unwrap();
|
||||
CellChangesetPB {
|
||||
grid_id: params.cell_identifier.view_id,
|
||||
row_id: params.cell_identifier.row_id,
|
||||
field_id: params.cell_identifier.field_id,
|
||||
type_cell_data: content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
@ -432,13 +417,13 @@ impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPB {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct SelectOptionCellChangeset {
|
||||
pub insert_option_ids: Vec<String>,
|
||||
pub delete_option_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl FromCellChangeset for SelectOptionCellChangeset {
|
||||
impl FromCellChangesetString for SelectOptionCellChangeset {
|
||||
fn from_changeset(changeset: String) -> FlowyResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -447,6 +432,12 @@ impl FromCellChangeset for SelectOptionCellChangeset {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCellChangesetString for SelectOptionCellChangeset {
|
||||
fn to_cell_changeset_str(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectOptionCellChangeset {
|
||||
pub fn from_insert_option_id(option_id: &str) -> Self {
|
||||
SelectOptionCellChangeset {
|
||||
@ -475,10 +466,6 @@ impl SelectOptionCellChangeset {
|
||||
delete_option_ids: option_ids,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_str(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// [SelectOptionCellDataPB] contains a list of user's selected options and a list of all the options
|
||||
@ -512,7 +499,7 @@ pub struct SelectOptionChangesetPB {
|
||||
}
|
||||
|
||||
pub struct SelectOptionChangeset {
|
||||
pub cell_identifier: CellPathParams,
|
||||
pub cell_path: CellPathParams,
|
||||
pub insert_options: Vec<SelectOptionPB>,
|
||||
pub update_options: Vec<SelectOptionPB>,
|
||||
pub delete_options: Vec<SelectOptionPB>,
|
||||
@ -524,7 +511,7 @@ impl TryInto<SelectOptionChangeset> for SelectOptionChangesetPB {
|
||||
fn try_into(self) -> Result<SelectOptionChangeset, Self::Error> {
|
||||
let cell_identifier = self.cell_identifier.try_into()?;
|
||||
Ok(SelectOptionChangeset {
|
||||
cell_identifier,
|
||||
cell_path: cell_identifier,
|
||||
insert_options: self.insert_options,
|
||||
update_options: self.update_options,
|
||||
delete_options: self.delete_options,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellDataDecoder, FromCellChangeset, FromCellString};
|
||||
use crate::services::cell::{CellDataDecoder, FromCellChangesetString, FromCellString, ToCellChangesetString};
|
||||
|
||||
use crate::services::filter::FromFilterString;
|
||||
use bytes::Bytes;
|
||||
@ -23,10 +23,10 @@ pub trait TypeOption {
|
||||
type CellData: FromCellString + ToString + Default + Send + Sync + Clone + 'static;
|
||||
|
||||
/// Represents as the corresponding field type cell changeset.
|
||||
/// The changeset must implements the `FromCellChangeset` trait. The `CellChangeset` is implemented
|
||||
/// for `String`.
|
||||
/// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait.
|
||||
/// These two traits are auto implemented for `String`.
|
||||
///
|
||||
type CellChangeset: FromCellChangeset;
|
||||
type CellChangeset: FromCellChangesetString + ToCellChangesetString;
|
||||
|
||||
/// For the moment, the protobuf type only be used in the FFI of `Dart`. If the decoded cell
|
||||
/// struct is just a `String`, then use the `StrCellData` as its `CellProtobufType`.
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{
|
||||
AtomicCellDataCache, AtomicCellFilterCache, CellDataChangeset, CellDataDecoder, CellProtobufBlob,
|
||||
FromCellChangeset, FromCellString, TypeCellData,
|
||||
FromCellChangesetString, FromCellString, TypeCellData,
|
||||
};
|
||||
use crate::services::field::{
|
||||
default_order, CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, MultiSelectTypeOptionPB,
|
||||
|
@ -77,7 +77,7 @@ impl FilterController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "schedule_filter_task", level = "trace", skip(self))]
|
||||
async fn gen_task(&mut self, task_type: FilterEvent, qos: QualityOfService) {
|
||||
async fn gen_task(&self, task_type: FilterEvent, qos: QualityOfService) {
|
||||
let task_id = self.task_scheduler.read().await.next_task_id();
|
||||
let task = Task::new(&self.handler_id, task_id, TaskContent::Text(task_type.to_string()), qos);
|
||||
self.task_scheduler.write().await.add_task(task);
|
||||
@ -148,9 +148,7 @@ impl FilterController {
|
||||
}
|
||||
}
|
||||
|
||||
let _ = self
|
||||
.notifier
|
||||
.send(GridViewChanged::DidReceiveFilterResult(notification));
|
||||
let _ = self.notifier.send(GridViewChanged::FilterNotification(notification));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -186,14 +184,12 @@ impl FilterController {
|
||||
visible_rows,
|
||||
};
|
||||
tracing::Span::current().record("filter_result", &format!("{:?}", ¬ification).as_str());
|
||||
let _ = self
|
||||
.notifier
|
||||
.send(GridViewChanged::DidReceiveFilterResult(notification));
|
||||
let _ = self.notifier.send(GridViewChanged::FilterNotification(notification));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn did_receive_row_changed(&mut self, row_id: &str) {
|
||||
pub async fn did_receive_row_changed(&self, row_id: &str) {
|
||||
self.gen_task(
|
||||
FilterEvent::RowDidChanged(row_id.to_string()),
|
||||
QualityOfService::UserInteractive,
|
||||
|
@ -24,6 +24,10 @@ impl TaskHandler for FilterTaskHandler {
|
||||
&self.handler_id
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> &str {
|
||||
"FilterTaskHandler"
|
||||
}
|
||||
|
||||
fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
|
||||
let filter_controller = self.filter_controller.clone();
|
||||
Box::pin(async move {
|
||||
@ -33,7 +37,7 @@ impl TaskHandler for FilterTaskHandler {
|
||||
.await
|
||||
.process(&predicate)
|
||||
.await
|
||||
.map_err(anyhow::Error::from);
|
||||
.map_err(anyhow::Error::from)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ use crate::manager::GridUser;
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::cell::{
|
||||
apply_cell_data_changeset, decode_type_cell_data, stringify_cell_data, AnyTypeCache, AtomicCellDataCache,
|
||||
CellProtobufBlob, TypeCellData,
|
||||
CellProtobufBlob, ToCellChangesetString, TypeCellData,
|
||||
};
|
||||
use crate::services::field::{
|
||||
default_type_option_builder_from_type, transform_type_option, type_option_builder_from_bytes, FieldBuilder,
|
||||
@ -31,6 +31,7 @@ use grid_rev_model::*;
|
||||
use lib_infra::future::{to_fut, FutureResult};
|
||||
use lib_ot::core::EmptyAttributes;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{broadcast, RwLock};
|
||||
|
||||
@ -405,16 +406,20 @@ impl GridRevisionEditor {
|
||||
/// Returns all the rows in this block.
|
||||
pub async fn get_row_pbs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<RowPB>> {
|
||||
let rows = self.view_manager.get_row_revs(view_id, block_id).await?;
|
||||
let rows = self
|
||||
.view_manager
|
||||
.filter_rows(view_id, block_id, rows)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|row_rev| RowPB::from(&row_rev))
|
||||
.collect();
|
||||
let rows = rows.into_iter().map(|row_rev| RowPB::from(&row_rev)).collect();
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
pub async fn get_all_row_revs(&self, view_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let mut all_rows = vec![];
|
||||
let blocks = self.block_manager.get_blocks(None).await?;
|
||||
for block in blocks {
|
||||
let rows = self.view_manager.get_row_revs(view_id, &block.block_id).await?;
|
||||
all_rows.extend(rows);
|
||||
}
|
||||
Ok(all_rows)
|
||||
}
|
||||
|
||||
pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<Arc<RowRevision>>> {
|
||||
match self.block_manager.get_row_rev(row_id).await? {
|
||||
None => Ok(None),
|
||||
@ -458,7 +463,7 @@ impl GridRevisionEditor {
|
||||
))
|
||||
};
|
||||
|
||||
display_str().await.unwrap_or("".to_string())
|
||||
display_str().await.unwrap_or_else(|| "".to_string())
|
||||
}
|
||||
|
||||
pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellProtobufBlob> {
|
||||
@ -488,52 +493,45 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn update_cell_with_changeset(&self, cell_changeset: CellChangesetPB) -> FlowyResult<()> {
|
||||
let CellChangesetPB {
|
||||
grid_id,
|
||||
row_id,
|
||||
field_id,
|
||||
type_cell_data: mut content,
|
||||
} = cell_changeset;
|
||||
|
||||
match self.grid_pad.read().await.get_field_rev(&field_id) {
|
||||
pub async fn update_cell_with_changeset<T: ToCellChangesetString>(
|
||||
&self,
|
||||
row_id: &str,
|
||||
field_id: &str,
|
||||
cell_changeset: T,
|
||||
) -> FlowyResult<()> {
|
||||
match self.grid_pad.read().await.get_field_rev(field_id) {
|
||||
None => {
|
||||
let msg = format!("Field:{} not found", &field_id);
|
||||
Err(FlowyError::internal().context(msg))
|
||||
}
|
||||
Some((_, field_rev)) => {
|
||||
tracing::trace!("field changeset: id:{} / value:{:?}", &field_id, content);
|
||||
let cell_rev = self.get_cell_rev(&row_id, &field_id).await?;
|
||||
tracing::trace!("Cell changeset: id:{} / value:{:?}", &field_id, cell_changeset);
|
||||
let cell_rev = self.get_cell_rev(row_id, field_id).await?;
|
||||
// Update the changeset.data property with the return value.
|
||||
content = apply_cell_data_changeset(content, cell_rev, field_rev, Some(self.cell_data_cache.clone()))?;
|
||||
let type_cell_data =
|
||||
apply_cell_data_changeset(cell_changeset, cell_rev, field_rev, Some(self.cell_data_cache.clone()))?;
|
||||
let cell_changeset = CellChangesetPB {
|
||||
grid_id,
|
||||
row_id: row_id.clone(),
|
||||
field_id: field_id.clone(),
|
||||
type_cell_data: content,
|
||||
grid_id: self.grid_id.clone(),
|
||||
row_id: row_id.to_owned(),
|
||||
field_id: field_id.to_owned(),
|
||||
type_cell_data,
|
||||
};
|
||||
let _ = self.block_manager.update_cell(cell_changeset).await?;
|
||||
self.view_manager.did_update_cell(&row_id).await;
|
||||
self.view_manager.did_update_cell(row_id).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn update_cell<T: ToString>(
|
||||
pub async fn update_cell<T: ToCellChangesetString>(
|
||||
&self,
|
||||
grid_id: String,
|
||||
row_id: String,
|
||||
field_id: String,
|
||||
content: T,
|
||||
cell_changeset: T,
|
||||
) -> FlowyResult<()> {
|
||||
self.update_cell_with_changeset(CellChangesetPB {
|
||||
grid_id,
|
||||
row_id,
|
||||
field_id,
|
||||
type_cell_data: content.to_string(),
|
||||
})
|
||||
.await
|
||||
self.update_cell_with_changeset(&row_id, &field_id, cell_changeset)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_block_meta_revs(&self) -> FlowyResult<Vec<Arc<GridBlockMetaRevision>>> {
|
||||
|
@ -92,7 +92,7 @@ impl RevisionSnapshotDiskCache for SQLiteGridRevisionSnapshotPersistence {
|
||||
Ok(Some(latest_record.into()))
|
||||
}
|
||||
}
|
||||
//noinspection ALL
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
|
||||
#[table_name = "grid_rev_snapshot"]
|
||||
#[primary_key("snapshot_id")]
|
||||
|
@ -1,52 +1,61 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use crate::entities::FieldType;
|
||||
#[allow(unused_attributes)]
|
||||
use crate::entities::SortChangesetNotificationPB;
|
||||
use crate::services::sort::{SortChangeset, SortType};
|
||||
use flowy_task::TaskDispatcher;
|
||||
use grid_rev_model::{CellRevision, FieldRevision, RowRevision, SortCondition, SortRevision};
|
||||
use lib_infra::future::Fut;
|
||||
|
||||
use crate::services::cell::{AtomicCellDataCache, TypeCellData};
|
||||
use crate::services::field::{default_order, TypeOptionCellExt};
|
||||
use crate::services::sort::{ReorderAllRowsResult, ReorderSingleRowResult, SortChangeset, SortType};
|
||||
use crate::services::view_editor::{GridViewChanged, GridViewChangedNotifier};
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
|
||||
use grid_rev_model::{CellRevision, FieldRevision, RowRevision, SortCondition, SortRevision};
|
||||
use lib_infra::future::Fut;
|
||||
use rayon::prelude::ParallelSliceMut;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub trait SortDelegate: Send + Sync {
|
||||
fn get_sort_rev(&self, sort_type: SortType) -> Fut<Option<Arc<SortRevision>>>;
|
||||
/// Returns all the rows after applying grid's filter
|
||||
fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>>;
|
||||
fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
|
||||
fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
|
||||
}
|
||||
|
||||
pub struct SortController {
|
||||
view_id: String,
|
||||
handler_id: String,
|
||||
delegate: Box<dyn SortDelegate>,
|
||||
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
||||
sorts: Vec<Arc<SortRevision>>,
|
||||
cell_data_cache: AtomicCellDataCache,
|
||||
row_index_cache: HashMap<String, usize>,
|
||||
notifier: GridViewChangedNotifier,
|
||||
}
|
||||
|
||||
impl SortController {
|
||||
pub fn new<T>(
|
||||
_view_id: &str,
|
||||
view_id: &str,
|
||||
handler_id: &str,
|
||||
delegate: T,
|
||||
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
||||
cell_data_cache: AtomicCellDataCache,
|
||||
notifier: GridViewChangedNotifier,
|
||||
) -> Self
|
||||
where
|
||||
T: SortDelegate + 'static,
|
||||
{
|
||||
Self {
|
||||
view_id: view_id.to_string(),
|
||||
handler_id: handler_id.to_string(),
|
||||
delegate: Box::new(delegate),
|
||||
task_scheduler,
|
||||
sorts: vec![],
|
||||
cell_data_cache,
|
||||
row_index_cache: Default::default(),
|
||||
notifier,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,65 +67,139 @@ impl SortController {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn sort_rows(&self, rows: &mut Vec<Arc<RowRevision>>) {
|
||||
pub async fn did_receive_row_changed(&self, row_id: &str) {
|
||||
let task_type = SortEvent::RowDidChanged(row_id.to_string());
|
||||
self.gen_task(task_type, QualityOfService::Background).await;
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "receive_sort_task_result", level = "trace", skip_all, err)]
|
||||
pub async fn process(&mut self, predicate: &str) -> FlowyResult<()> {
|
||||
let event_type = SortEvent::from_str(predicate).unwrap();
|
||||
let mut row_revs = self.delegate.get_row_revs().await;
|
||||
match event_type {
|
||||
SortEvent::SortDidChanged => {
|
||||
self.sort_rows(&mut row_revs).await;
|
||||
let row_orders = row_revs
|
||||
.iter()
|
||||
.map(|row_rev| row_rev.id.clone())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let notification = ReorderAllRowsResult {
|
||||
view_id: self.view_id.clone(),
|
||||
row_orders,
|
||||
};
|
||||
|
||||
let _ = self
|
||||
.notifier
|
||||
.send(GridViewChanged::ReorderAllRowsNotification(notification));
|
||||
}
|
||||
SortEvent::RowDidChanged(row_id) => {
|
||||
let old_row_index = self.row_index_cache.get(&row_id).cloned();
|
||||
self.sort_rows(&mut row_revs).await;
|
||||
let new_row_index = self.row_index_cache.get(&row_id).cloned();
|
||||
match (old_row_index, new_row_index) {
|
||||
(Some(old_row_index), Some(new_row_index)) => {
|
||||
if old_row_index == new_row_index {
|
||||
return Ok(());
|
||||
}
|
||||
let notification = ReorderSingleRowResult {
|
||||
view_id: self.view_id.clone(),
|
||||
old_index: old_row_index,
|
||||
new_index: new_row_index,
|
||||
};
|
||||
let _ = self
|
||||
.notifier
|
||||
.send(GridViewChanged::ReorderSingleRowNotification(notification));
|
||||
}
|
||||
_ => tracing::trace!("The row index cache is outdated"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "schedule_sort_task", level = "trace", skip(self))]
|
||||
async fn gen_task(&self, task_type: SortEvent, qos: QualityOfService) {
|
||||
if self.sorts.is_empty() {
|
||||
return;
|
||||
}
|
||||
let task_id = self.task_scheduler.read().await.next_task_id();
|
||||
let task = Task::new(&self.handler_id, task_id, TaskContent::Text(task_type.to_string()), qos);
|
||||
self.task_scheduler.write().await.add_task(task);
|
||||
}
|
||||
|
||||
pub async fn sort_rows(&mut self, rows: &mut Vec<Arc<RowRevision>>) {
|
||||
if self.sorts.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let field_revs = self.delegate.get_field_revs(None).await;
|
||||
rows.par_sort_by(|left, right| cmp_row(left, right, &self.sorts, &field_revs, &self.cell_data_cache));
|
||||
for sort in self.sorts.iter() {
|
||||
rows.par_sort_by(|left, right| cmp_row(left, right, sort, &field_revs, &self.cell_data_cache));
|
||||
}
|
||||
rows.iter().enumerate().for_each(|(index, row)| {
|
||||
self.row_index_cache.insert(row.id.to_string(), index);
|
||||
});
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn did_receive_changes(&mut self, changeset: SortChangeset) -> Option<SortChangesetNotificationPB> {
|
||||
pub async fn did_receive_changes(&mut self, changeset: SortChangeset) -> SortChangesetNotificationPB {
|
||||
let mut notification = SortChangesetNotificationPB::default();
|
||||
if let Some(insert_sort) = changeset.insert_sort {
|
||||
if let Some(sort) = self.delegate.get_sort_rev(insert_sort).await {
|
||||
notification.insert_sorts.push(sort.as_ref().into());
|
||||
self.sorts.push(sort);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(delete_sort_type) = changeset.delete_sort {
|
||||
if let Some(index) = self.sorts.iter().position(|sort| sort.id == delete_sort_type.sort_id) {
|
||||
self.sorts.remove(index);
|
||||
let sort = self.sorts.remove(index);
|
||||
notification.delete_sorts.push(sort.as_ref().into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_update_sort) = changeset.update_sort {
|
||||
//
|
||||
if let Some(update_sort) = changeset.update_sort {
|
||||
if let Some(updated_sort) = self.delegate.get_sort_rev(update_sort).await {
|
||||
notification.update_sorts.push(updated_sort.as_ref().into());
|
||||
if let Some(index) = self.sorts.iter().position(|sort| sort.id == updated_sort.id) {
|
||||
self.sorts[index] = updated_sort;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
if !notification.insert_sorts.is_empty() || !notification.delete_sorts.is_empty() {
|
||||
self.gen_task(SortEvent::SortDidChanged, QualityOfService::Background)
|
||||
.await;
|
||||
}
|
||||
notification
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_row(
|
||||
left: &Arc<RowRevision>,
|
||||
right: &Arc<RowRevision>,
|
||||
sorts: &[Arc<SortRevision>],
|
||||
sort: &Arc<SortRevision>,
|
||||
field_revs: &[Arc<FieldRevision>],
|
||||
cell_data_cache: &AtomicCellDataCache,
|
||||
) -> Ordering {
|
||||
let mut order = default_order();
|
||||
for sort in sorts.iter() {
|
||||
let cmp_order = match (left.cells.get(&sort.field_id), right.cells.get(&sort.field_id)) {
|
||||
(Some(left_cell), Some(right_cell)) => {
|
||||
let field_type: FieldType = sort.field_type.into();
|
||||
match field_revs.iter().find(|field_rev| field_rev.id == sort.field_id) {
|
||||
None => default_order(),
|
||||
Some(field_rev) => cmp_cell(left_cell, right_cell, field_rev, field_type, cell_data_cache),
|
||||
}
|
||||
let order = match (left.cells.get(&sort.field_id), right.cells.get(&sort.field_id)) {
|
||||
(Some(left_cell), Some(right_cell)) => {
|
||||
let field_type: FieldType = sort.field_type.into();
|
||||
match field_revs.iter().find(|field_rev| field_rev.id == sort.field_id) {
|
||||
None => default_order(),
|
||||
Some(field_rev) => cmp_cell(left_cell, right_cell, field_rev, field_type, cell_data_cache),
|
||||
}
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
_ => default_order(),
|
||||
};
|
||||
|
||||
if cmp_order.is_ne() {
|
||||
// If the cmp_order is not Ordering::Equal, then break the loop.
|
||||
order = match sort.condition {
|
||||
SortCondition::Ascending => cmp_order,
|
||||
SortCondition::Descending => cmp_order.reverse(),
|
||||
};
|
||||
break;
|
||||
}
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
_ => default_order(),
|
||||
};
|
||||
|
||||
match sort.condition {
|
||||
SortCondition::Ascending => order,
|
||||
SortCondition::Descending => order.reverse(),
|
||||
}
|
||||
order
|
||||
}
|
||||
|
||||
fn cmp_cell(
|
||||
@ -142,3 +225,21 @@ fn cmp_cell(
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
enum SortEvent {
|
||||
SortDidChanged,
|
||||
RowDidChanged(String),
|
||||
}
|
||||
|
||||
impl ToString for SortEvent {
|
||||
fn to_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SortEvent {
|
||||
type Err = serde_json::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,25 @@ impl std::convert::From<&Arc<FieldRevision>> for SortType {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
pub struct ReorderAllRowsResult {
|
||||
pub view_id: String,
|
||||
pub row_orders: Vec<String>,
|
||||
}
|
||||
|
||||
impl ReorderAllRowsResult {
|
||||
pub fn new(view_id: String, row_orders: Vec<String>) -> Self {
|
||||
Self { view_id, row_orders }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ReorderSingleRowResult {
|
||||
pub view_id: String,
|
||||
pub old_index: usize,
|
||||
pub new_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SortChangeset {
|
||||
pub(crate) insert_sort: Option<SortType>,
|
||||
|
@ -24,7 +24,22 @@ impl TaskHandler for SortTaskHandler {
|
||||
&self.handler_id
|
||||
}
|
||||
|
||||
fn run(&self, _content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
|
||||
todo!();
|
||||
fn handler_name(&self) -> &str {
|
||||
"SortTaskHandler"
|
||||
}
|
||||
|
||||
fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
|
||||
let sort_controller = self.sort_controller.clone();
|
||||
Box::pin(async move {
|
||||
if let TaskContent::Text(predicate) = content {
|
||||
let _ = sort_controller
|
||||
.write()
|
||||
.await
|
||||
.process(&predicate)
|
||||
.await
|
||||
.map_err(anyhow::Error::from)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridDartNotification};
|
||||
use crate::entities::GridRowsVisibilityChangesetPB;
|
||||
use crate::entities::{GridRowsVisibilityChangesetPB, ReorderAllRowsPB, ReorderSingleRowPB};
|
||||
use crate::services::filter::FilterResultNotification;
|
||||
use crate::services::sort::{ReorderAllRowsResult, ReorderSingleRowResult};
|
||||
use async_stream::stream;
|
||||
use futures::stream::StreamExt;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum GridViewChanged {
|
||||
DidReceiveFilterResult(FilterResultNotification),
|
||||
FilterNotification(FilterResultNotification),
|
||||
ReorderAllRowsNotification(ReorderAllRowsResult),
|
||||
ReorderSingleRowNotification(ReorderSingleRowResult),
|
||||
}
|
||||
|
||||
pub type GridViewChangedNotifier = broadcast::Sender<GridViewChanged>;
|
||||
@ -27,7 +30,7 @@ impl GridViewChangedReceiverRunner {
|
||||
stream
|
||||
.for_each(|changed| async {
|
||||
match changed {
|
||||
GridViewChanged::DidReceiveFilterResult(notification) => {
|
||||
GridViewChanged::FilterNotification(notification) => {
|
||||
let changeset = GridRowsVisibilityChangesetPB {
|
||||
view_id: notification.view_id,
|
||||
visible_rows: notification.visible_rows,
|
||||
@ -41,6 +44,23 @@ impl GridViewChangedReceiverRunner {
|
||||
.payload(changeset)
|
||||
.send()
|
||||
}
|
||||
GridViewChanged::ReorderAllRowsNotification(notification) => {
|
||||
let row_orders = ReorderAllRowsPB {
|
||||
row_orders: notification.row_orders,
|
||||
};
|
||||
send_dart_notification(¬ification.view_id, GridDartNotification::DidReorderRows)
|
||||
.payload(row_orders)
|
||||
.send()
|
||||
}
|
||||
GridViewChanged::ReorderSingleRowNotification(notification) => {
|
||||
let reorder_row = ReorderSingleRowPB {
|
||||
old_index: notification.old_index as i32,
|
||||
new_index: notification.new_index as i32,
|
||||
};
|
||||
send_dart_notification(¬ification.view_id, GridDartNotification::DidReorderSingleRow)
|
||||
.payload(reorder_row)
|
||||
.send()
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
@ -112,24 +112,26 @@ impl GridViewRevisionEditor {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let sort_controller = make_sort_controller(
|
||||
&view_id,
|
||||
delegate.clone(),
|
||||
view_rev_pad.clone(),
|
||||
cell_data_cache.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let user_id = user_id.to_owned();
|
||||
let group_controller = Arc::new(RwLock::new(group_controller));
|
||||
let filter_controller = make_filter_controller(
|
||||
&view_id,
|
||||
delegate.clone(),
|
||||
notifier.clone(),
|
||||
cell_data_cache,
|
||||
cell_data_cache.clone(),
|
||||
view_rev_pad.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let sort_controller = make_sort_controller(
|
||||
&view_id,
|
||||
delegate.clone(),
|
||||
notifier.clone(),
|
||||
filter_controller.clone(),
|
||||
view_rev_pad.clone(),
|
||||
cell_data_cache,
|
||||
)
|
||||
.await;
|
||||
Ok(Self {
|
||||
pad: view_rev_pad,
|
||||
user_id,
|
||||
@ -181,12 +183,11 @@ impl GridViewRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn sort_rows(&self, rows: &mut Vec<Arc<RowRevision>>) {
|
||||
self.sort_controller.read().await.sort_rows(rows).await
|
||||
self.sort_controller.write().await.sort_rows(rows).await
|
||||
}
|
||||
|
||||
pub async fn filter_rows(&self, _block_id: &str, mut rows: Vec<Arc<RowRevision>>) -> Vec<Arc<RowRevision>> {
|
||||
self.filter_controller.write().await.filter_row_revs(&mut rows).await;
|
||||
rows
|
||||
pub async fn filter_rows(&self, _block_id: &str, rows: &mut Vec<Arc<RowRevision>>) {
|
||||
self.filter_controller.write().await.filter_row_revs(rows).await;
|
||||
}
|
||||
|
||||
pub async fn duplicate_view_data(&self) -> FlowyResult<String> {
|
||||
@ -260,9 +261,11 @@ impl GridViewRevisionEditor {
|
||||
}
|
||||
|
||||
let filter_controller = self.filter_controller.clone();
|
||||
let sort_controller = self.sort_controller.clone();
|
||||
let row_id = row_rev.id.clone();
|
||||
tokio::spawn(async move {
|
||||
filter_controller.write().await.did_receive_row_changed(&row_id).await;
|
||||
filter_controller.read().await.did_receive_row_changed(&row_id).await;
|
||||
sort_controller.read().await.did_receive_row_changed(&row_id).await;
|
||||
});
|
||||
}
|
||||
|
||||
@ -423,10 +426,9 @@ impl GridViewRevisionEditor {
|
||||
.did_receive_changes(SortChangeset::from_insert(sort_type))
|
||||
.await
|
||||
};
|
||||
drop(sort_controller);
|
||||
|
||||
if let Some(changeset) = changeset {
|
||||
self.notify_did_update_sort(changeset).await;
|
||||
}
|
||||
self.notify_did_update_sort(changeset).await;
|
||||
Ok(sort_rev)
|
||||
}
|
||||
|
||||
@ -446,9 +448,7 @@ impl GridViewRevisionEditor {
|
||||
})
|
||||
.await?;
|
||||
|
||||
if changeset.is_some() {
|
||||
self.notify_did_update_sort(changeset.unwrap()).await;
|
||||
}
|
||||
self.notify_did_update_sort(changeset).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -495,6 +495,7 @@ impl GridViewRevisionEditor {
|
||||
.did_receive_changes(FilterChangeset::from_insert(filter_type))
|
||||
.await
|
||||
};
|
||||
drop(filter_controller);
|
||||
|
||||
if let Some(changeset) = changeset {
|
||||
self.notify_did_update_filter(changeset).await;
|
||||
@ -612,9 +613,11 @@ impl GridViewRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn notify_did_update_sort(&self, changeset: SortChangesetNotificationPB) {
|
||||
send_dart_notification(&changeset.view_id, GridDartNotification::DidUpdateSort)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
if !changeset.is_empty() {
|
||||
send_dart_notification(&changeset.view_id, GridDartNotification::DidUpdateSort)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
|
||||
@ -756,6 +759,8 @@ async fn make_filter_controller(
|
||||
async fn make_sort_controller(
|
||||
view_id: &str,
|
||||
delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
notifier: GridViewChangedNotifier,
|
||||
filter_controller: Arc<RwLock<FilterController>>,
|
||||
pad: Arc<RwLock<GridViewRevisionPad>>,
|
||||
cell_data_cache: AtomicCellDataCache,
|
||||
) -> Arc<RwLock<SortController>> {
|
||||
@ -763,6 +768,7 @@ async fn make_sort_controller(
|
||||
let sort_delegate = GridViewSortDelegateImpl {
|
||||
editor_delegate: delegate.clone(),
|
||||
view_revision_pad: pad,
|
||||
filter_controller,
|
||||
};
|
||||
let task_scheduler = delegate.get_task_scheduler();
|
||||
let sort_controller = Arc::new(RwLock::new(SortController::new(
|
||||
@ -771,6 +777,7 @@ async fn make_sort_controller(
|
||||
sort_delegate,
|
||||
task_scheduler.clone(),
|
||||
cell_data_cache,
|
||||
notifier,
|
||||
)));
|
||||
task_scheduler
|
||||
.write()
|
||||
|
@ -60,22 +60,11 @@ impl GridViewManager {
|
||||
pub async fn get_row_revs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let mut row_revs = self.delegate.get_row_revs(Some(vec![block_id.to_owned()])).await;
|
||||
if let Ok(view_editor) = self.get_view_editor(view_id).await {
|
||||
view_editor.filter_rows(block_id, &mut row_revs).await;
|
||||
view_editor.sort_rows(&mut row_revs).await;
|
||||
}
|
||||
Ok(row_revs)
|
||||
}
|
||||
|
||||
pub async fn filter_rows(
|
||||
&self,
|
||||
view_id: &str,
|
||||
block_id: &str,
|
||||
rows: Vec<Arc<RowRevision>>,
|
||||
) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let rows = match self.get_view_editor(view_id).await {
|
||||
Ok(view_editor) => view_editor.filter_rows(block_id, rows).await,
|
||||
Err(_) => rows,
|
||||
};
|
||||
Ok(rows)
|
||||
Ok(row_revs)
|
||||
}
|
||||
|
||||
pub async fn duplicate_grid_view(&self) -> FlowyResult<String> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::{GridLayout, GridLayoutPB, GridSettingPB};
|
||||
use crate::services::filter::{FilterDelegate, FilterType};
|
||||
use crate::services::filter::{FilterController, FilterDelegate, FilterType};
|
||||
use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter};
|
||||
use crate::services::row::GridBlockRowRevision;
|
||||
use crate::services::sort::{SortDelegate, SortType};
|
||||
@ -169,6 +169,7 @@ impl FilterDelegate for GridViewFilterDelegateImpl {
|
||||
pub(crate) struct GridViewSortDelegateImpl {
|
||||
pub(crate) editor_delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
pub(crate) view_revision_pad: Arc<RwLock<GridViewRevisionPad>>,
|
||||
pub(crate) filter_controller: Arc<RwLock<FilterController>>,
|
||||
}
|
||||
|
||||
impl SortDelegate for GridViewSortDelegateImpl {
|
||||
@ -187,6 +188,16 @@ impl SortDelegate for GridViewSortDelegateImpl {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>> {
|
||||
let filter_controller = self.filter_controller.clone();
|
||||
let editor_delegate = self.editor_delegate.clone();
|
||||
to_fut(async move {
|
||||
let mut row_revs = editor_delegate.get_row_revs(None).await;
|
||||
filter_controller.write().await.filter_row_revs(&mut row_revs).await;
|
||||
row_revs
|
||||
})
|
||||
}
|
||||
|
||||
fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>> {
|
||||
self.editor_delegate.get_field_rev(field_id)
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ use grid_rev_model::RowChangeset;
|
||||
async fn grid_create_row_count_test() {
|
||||
let mut test = GridRowTest::new().await;
|
||||
let scripts = vec![
|
||||
AssertRowCount(5),
|
||||
AssertRowCount(6),
|
||||
CreateEmptyRow,
|
||||
CreateEmptyRow,
|
||||
CreateRow {
|
||||
row_rev: test.row_builder().build(),
|
||||
},
|
||||
AssertRowCount(8),
|
||||
AssertRowCount(9),
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
@ -30,12 +30,12 @@ async fn grid_update_row() {
|
||||
visibility: None,
|
||||
cell_by_field_id: Default::default(),
|
||||
};
|
||||
|
||||
let scripts = vec![AssertRowCount(5), CreateRow { row_rev }, UpdateRow { changeset }];
|
||||
let row_count = test.row_revs.len();
|
||||
let scripts = vec![CreateRow { row_rev }, UpdateRow { changeset }];
|
||||
test.run_scripts(scripts).await;
|
||||
|
||||
let expected_row = test.last_row().unwrap();
|
||||
let scripts = vec![AssertRow { expected_row }, AssertRowCount(6)];
|
||||
let scripts = vec![AssertRow { expected_row }, AssertRowCount(row_count + 1)];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
@ -45,20 +45,20 @@ async fn grid_delete_row() {
|
||||
let row_1 = test.row_builder().build();
|
||||
let row_2 = test.row_builder().build();
|
||||
let row_ids = vec![row_1.id.clone(), row_2.id.clone()];
|
||||
let row_count = test.row_revs.len() as i32;
|
||||
let scripts = vec![
|
||||
AssertRowCount(5),
|
||||
CreateRow { row_rev: row_1 },
|
||||
CreateRow { row_rev: row_2 },
|
||||
AssertBlockCount(1),
|
||||
AssertBlock {
|
||||
block_index: 0,
|
||||
row_count: 7,
|
||||
row_count: row_count + 2,
|
||||
start_row_index: 0,
|
||||
},
|
||||
DeleteRows { row_ids },
|
||||
AssertBlock {
|
||||
block_index: 0,
|
||||
row_count: 5,
|
||||
row_count,
|
||||
start_row_index: 0,
|
||||
},
|
||||
];
|
||||
|
@ -113,7 +113,7 @@ impl GridRowTest {
|
||||
expected,
|
||||
} => {
|
||||
let id = CellPathParams {
|
||||
view_id: self.grid_id.clone(),
|
||||
view_id: self.view_id.clone(),
|
||||
field_id,
|
||||
row_id,
|
||||
};
|
||||
|
@ -24,17 +24,19 @@ impl GridCellTest {
|
||||
pub async fn run_script(&mut self, script: CellScript) {
|
||||
// let grid_manager = self.sdk.grid_manager.clone();
|
||||
// let pool = self.sdk.user_session.db_pool().unwrap();
|
||||
let rev_manager = self.editor.rev_manager();
|
||||
let _cache = rev_manager.revision_cache().await;
|
||||
// let rev_manager = self.editor.rev_manager();
|
||||
// let _cache = rev_manager.revision_cache().await;
|
||||
|
||||
match script {
|
||||
CellScript::UpdateCell { changeset, is_err } => {
|
||||
let result = self.editor.update_cell_with_changeset(changeset).await;
|
||||
let result = self
|
||||
.editor
|
||||
.update_cell_with_changeset(&changeset.row_id, &changeset.field_id, changeset.type_cell_data)
|
||||
.await;
|
||||
if is_err {
|
||||
assert!(result.is_err())
|
||||
} else {
|
||||
let _ = result.unwrap();
|
||||
self.row_revs = self.get_row_revs().await;
|
||||
}
|
||||
} // CellScript::AssertGridRevisionPad => {
|
||||
// sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
|
||||
|
@ -2,6 +2,7 @@ use crate::grid::cell_test::script::CellScript::*;
|
||||
use crate::grid::cell_test::script::GridCellTest;
|
||||
use crate::grid::field_test::util::make_date_cell_string;
|
||||
use flowy_grid::entities::{CellChangesetPB, FieldType};
|
||||
use flowy_grid::services::cell::ToCellChangesetString;
|
||||
use flowy_grid::services::field::selection_type_option::SelectOptionCellChangeset;
|
||||
use flowy_grid::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
|
||||
|
||||
@ -25,15 +26,18 @@ async fn grid_cell_update() {
|
||||
FieldType::DateTime => make_date_cell_string("123"),
|
||||
FieldType::SingleSelect => {
|
||||
let type_option = SingleSelectTypeOptionPB::from(field_rev);
|
||||
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str()
|
||||
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
|
||||
.to_cell_changeset_str()
|
||||
}
|
||||
FieldType::MultiSelect => {
|
||||
let type_option = MultiSelectTypeOptionPB::from(field_rev);
|
||||
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str()
|
||||
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
|
||||
.to_cell_changeset_str()
|
||||
}
|
||||
FieldType::Checklist => {
|
||||
let type_option = ChecklistTypeOptionPB::from(field_rev);
|
||||
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str()
|
||||
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
|
||||
.to_cell_changeset_str()
|
||||
}
|
||||
FieldType::Checkbox => "1".to_string(),
|
||||
FieldType::URL => "1".to_string(),
|
||||
|
@ -49,7 +49,7 @@ impl GridFieldTest {
|
||||
}
|
||||
|
||||
pub fn view_id(&self) -> String {
|
||||
self.grid_id.clone()
|
||||
self.view_id.clone()
|
||||
}
|
||||
|
||||
pub fn field_count(&self) -> usize {
|
||||
@ -100,7 +100,7 @@ impl GridFieldTest {
|
||||
FieldScript::UpdateTypeOption { field_id, type_option } => {
|
||||
//
|
||||
self.editor
|
||||
.update_field_type_option(&self.grid_id, &field_id, type_option, None)
|
||||
.update_field_type_option(&self.view_id, &field_id, type_option, None)
|
||||
.await
|
||||
.unwrap();
|
||||
self.field_revs = self.editor.get_field_revs(None).await.unwrap();
|
||||
|
@ -9,7 +9,7 @@ async fn grid_filter_checklist_is_incomplete_test() {
|
||||
CreateChecklistFilter {
|
||||
condition: ChecklistFilterConditionPB::IsIncomplete,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 4 },
|
||||
AssertNumberOfVisibleRows { expected: 5 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ async fn grid_filter_date_after_test() {
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 2 },
|
||||
AssertNumberOfVisibleRows { expected: 3 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
@ -42,7 +42,7 @@ async fn grid_filter_date_on_or_after_test() {
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 2 },
|
||||
AssertNumberOfVisibleRows { expected: 3 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ async fn grid_filter_number_is_not_empty_test() {
|
||||
condition: NumberFilterConditionPB::NumberIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 4 },
|
||||
AssertNumberOfVisibleRows { expected: 5 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ use crate::grid::grid_editor::GridEditorTest;
|
||||
|
||||
pub enum FilterScript {
|
||||
UpdateTextCell {
|
||||
row_index: usize,
|
||||
row_id: String,
|
||||
text: String,
|
||||
},
|
||||
UpdateSingleSelectCell {
|
||||
row_index: usize,
|
||||
row_id: String,
|
||||
option_id: String,
|
||||
},
|
||||
InsertFilter {
|
||||
@ -103,7 +103,7 @@ impl GridFilterTest {
|
||||
}
|
||||
|
||||
pub fn view_id(&self) -> String {
|
||||
self.grid_id.clone()
|
||||
self.view_id.clone()
|
||||
}
|
||||
|
||||
pub async fn get_all_filters(&self) -> Vec<FilterPB> {
|
||||
@ -118,13 +118,13 @@ impl GridFilterTest {
|
||||
|
||||
pub async fn run_script(&mut self, script: FilterScript) {
|
||||
match script {
|
||||
FilterScript::UpdateTextCell { row_index, text} => {
|
||||
FilterScript::UpdateTextCell { row_id, text} => {
|
||||
self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
|
||||
self.update_text_cell(row_index, &text).await;
|
||||
self.update_text_cell(row_id, &text).await;
|
||||
}
|
||||
FilterScript::UpdateSingleSelectCell { row_index, option_id} => {
|
||||
FilterScript::UpdateSingleSelectCell { row_id, option_id} => {
|
||||
self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
|
||||
self.update_single_select_cell(row_index, &option_id).await;
|
||||
self.update_single_select_cell(row_id, &option_id).await;
|
||||
}
|
||||
FilterScript::InsertFilter { payload } => {
|
||||
self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
|
||||
@ -241,13 +241,15 @@ impl GridFilterTest {
|
||||
match tokio::time::timeout(Duration::from_secs(2), receiver.recv()).await {
|
||||
Ok(changed) => {
|
||||
//
|
||||
match changed.unwrap() { GridViewChanged::DidReceiveFilterResult(changed) => {
|
||||
match changed.unwrap() { GridViewChanged::FilterNotification(changed) => {
|
||||
assert_eq!(changed.visible_rows.len(), visible_row_len, "visible rows not match");
|
||||
assert_eq!(changed.invisible_rows.len(), hide_row_len, "invisible rows not match");
|
||||
} }
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
panic!("Process task timeout: {:?}", e);
|
||||
panic!("Process filter task timeout: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -268,38 +270,6 @@ impl GridFilterTest {
|
||||
let _ = self.editor.create_or_update_filter(params).await.unwrap();
|
||||
}
|
||||
|
||||
async fn update_text_cell(&self, row_index: usize, content: &str) {
|
||||
let row_rev = &self.inner.row_revs[row_index];
|
||||
let field_rev = self.inner.field_revs.iter().find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
field_type == FieldType::RichText
|
||||
}).unwrap();
|
||||
let changeset =CellChangesetPB {
|
||||
grid_id: self.grid_id.clone(),
|
||||
row_id: row_rev.id.clone(),
|
||||
field_id: field_rev.id.clone(),
|
||||
type_cell_data: content.to_string(),
|
||||
};
|
||||
self.editor.update_cell_with_changeset(changeset).await.unwrap();
|
||||
|
||||
}
|
||||
async fn update_single_select_cell(&self, row_index: usize, option_id: &str) {
|
||||
let row_rev = &self.inner.row_revs[row_index];
|
||||
let field_rev = self.inner.field_revs.iter().find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
field_type == FieldType::SingleSelect
|
||||
}).unwrap();
|
||||
|
||||
let content = SelectOptionCellChangeset::from_insert_option_id(&option_id).to_str();
|
||||
let changeset =CellChangesetPB {
|
||||
grid_id: self.grid_id.clone(),
|
||||
row_id: row_rev.id.clone(),
|
||||
field_id: field_rev.id.clone(),
|
||||
type_cell_data: content,
|
||||
};
|
||||
self.editor.update_cell_with_changeset(changeset).await.unwrap();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ async fn grid_filter_multi_select_is_empty_test() {
|
||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||
option_ids: vec![],
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 2 },
|
||||
AssertNumberOfVisibleRows { expected: 3 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
@ -90,6 +90,7 @@ async fn grid_filter_single_select_is_test() {
|
||||
async fn grid_filter_single_select_is_test2() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let field_rev = test.get_first_field_rev(FieldType::SingleSelect);
|
||||
let row_revs = test.get_row_revs().await;
|
||||
let mut options = test.get_single_select_type_option(&field_rev.id).options;
|
||||
let option = options.remove(0);
|
||||
let scripts = vec![
|
||||
@ -99,12 +100,12 @@ async fn grid_filter_single_select_is_test2() {
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 2 },
|
||||
UpdateSingleSelectCell {
|
||||
row_index: 1,
|
||||
row_id: row_revs[1].id.clone(),
|
||||
option_id: option.id.clone(),
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 3 },
|
||||
UpdateSingleSelectCell {
|
||||
row_index: 1,
|
||||
row_id: row_revs[1].id.clone(),
|
||||
option_id: "".to_string(),
|
||||
},
|
||||
AssertFilterChanged {
|
||||
|
@ -14,7 +14,7 @@ async fn grid_filter_text_is_empty_test() {
|
||||
AssertFilterCount { count: 1 },
|
||||
AssertFilterChanged {
|
||||
visible_row_len: 0,
|
||||
hide_row_len: 4,
|
||||
hide_row_len: 5,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
@ -65,7 +65,7 @@ async fn grid_filter_is_text_test() {
|
||||
},
|
||||
AssertFilterChanged {
|
||||
visible_row_len: 0,
|
||||
hide_row_len: 4,
|
||||
hide_row_len: 5,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
@ -90,6 +90,8 @@ async fn grid_filter_contain_text_test() {
|
||||
#[tokio::test]
|
||||
async fn grid_filter_contain_text_test2() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let row_revs = test.row_revs.clone();
|
||||
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::Contains,
|
||||
@ -100,7 +102,7 @@ async fn grid_filter_contain_text_test2() {
|
||||
hide_row_len: 2,
|
||||
},
|
||||
UpdateTextCell {
|
||||
row_index: 1,
|
||||
row_id: row_revs[1].id.clone(),
|
||||
text: "ABC".to_string(),
|
||||
},
|
||||
AssertFilterChanged {
|
||||
@ -206,7 +208,7 @@ async fn grid_filter_delete_test() {
|
||||
filter_type: FilterType::from(&field_rev),
|
||||
},
|
||||
AssertFilterCount { count: 0 },
|
||||
AssertNumberOfVisibleRows { expected: 5 },
|
||||
AssertNumberOfVisibleRows { expected: 6 },
|
||||
])
|
||||
.await;
|
||||
}
|
||||
@ -214,6 +216,7 @@ async fn grid_filter_delete_test() {
|
||||
#[tokio::test]
|
||||
async fn grid_filter_update_empty_text_cell_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let row_revs = test.row_revs.clone();
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
@ -222,10 +225,10 @@ async fn grid_filter_update_empty_text_cell_test() {
|
||||
AssertFilterCount { count: 1 },
|
||||
AssertFilterChanged {
|
||||
visible_row_len: 0,
|
||||
hide_row_len: 4,
|
||||
hide_row_len: 5,
|
||||
},
|
||||
UpdateTextCell {
|
||||
row_index: 0,
|
||||
row_id: row_revs[0].id.clone(),
|
||||
text: "".to_string(),
|
||||
},
|
||||
AssertFilterChanged {
|
||||
|
@ -5,6 +5,7 @@ use crate::grid::block_test::util::GridRowTestBuilder;
|
||||
use bytes::Bytes;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid::entities::*;
|
||||
use flowy_grid::services::cell::ToCellChangesetString;
|
||||
use flowy_grid::services::field::SelectOptionPB;
|
||||
use flowy_grid::services::field::*;
|
||||
use flowy_grid::services::grid_editor::{GridRevisionEditor, GridRevisionSerde};
|
||||
@ -24,7 +25,7 @@ use tokio::time::sleep;
|
||||
|
||||
pub struct GridEditorTest {
|
||||
pub sdk: FlowySDKTest,
|
||||
pub grid_id: String,
|
||||
pub view_id: String,
|
||||
pub editor: Arc<GridRevisionEditor>,
|
||||
pub field_revs: Vec<Arc<FieldRevision>>,
|
||||
pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
|
||||
@ -61,7 +62,7 @@ impl GridEditorTest {
|
||||
let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
|
||||
let field_revs = editor.get_field_revs(None).await.unwrap();
|
||||
let block_meta_revs = editor.get_block_meta_revs().await.unwrap();
|
||||
let row_revs = editor.get_blocks(None).await.unwrap().pop().unwrap().row_revs;
|
||||
let row_pbs = editor.get_all_row_revs(&test.view.id).await.unwrap();
|
||||
assert_eq!(block_meta_revs.len(), 1);
|
||||
|
||||
// It seems like you should add the field in the make_test_grid() function.
|
||||
@ -71,18 +72,18 @@ impl GridEditorTest {
|
||||
let grid_id = test.view.id;
|
||||
Self {
|
||||
sdk,
|
||||
grid_id,
|
||||
view_id: grid_id,
|
||||
editor,
|
||||
field_revs,
|
||||
block_meta_revs,
|
||||
row_revs,
|
||||
row_revs: row_pbs,
|
||||
field_count: FieldType::COUNT,
|
||||
row_by_row_id: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
|
||||
self.editor.get_blocks(None).await.unwrap().pop().unwrap().row_revs
|
||||
self.editor.get_all_row_revs(&self.view_id).await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn grid_filters(&self) -> Vec<FilterPB> {
|
||||
@ -153,6 +154,48 @@ impl GridEditorTest {
|
||||
pub fn block_id(&self) -> &str {
|
||||
&self.block_meta_revs.last().unwrap().block_id
|
||||
}
|
||||
|
||||
pub async fn update_cell<T: ToCellChangesetString>(&mut self, field_id: &str, row_id: String, cell_changeset: T) {
|
||||
let field_rev = self
|
||||
.field_revs
|
||||
.iter()
|
||||
.find(|field_rev| field_rev.id == field_id)
|
||||
.unwrap();
|
||||
|
||||
self.editor
|
||||
.update_cell_with_changeset(&row_id, &field_rev.id, cell_changeset)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) async fn update_text_cell(&mut self, row_id: String, content: &str) {
|
||||
let field_rev = self
|
||||
.field_revs
|
||||
.iter()
|
||||
.find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
field_type == FieldType::RichText
|
||||
})
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
self.update_cell(&field_rev.id, row_id, content.to_string()).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_single_select_cell(&mut self, row_id: String, option_id: &str) {
|
||||
let field_rev = self
|
||||
.field_revs
|
||||
.iter()
|
||||
.find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
field_type == FieldType::SingleSelect
|
||||
})
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let cell_changeset = SelectOptionCellChangeset::from_insert_option_id(&option_id);
|
||||
self.update_cell(&field_rev.id, row_id, cell_changeset).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub const GOOGLE: &str = "Google";
|
||||
@ -243,7 +286,7 @@ fn make_test_grid() -> BuildGridContext {
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..5 {
|
||||
for i in 0..6 {
|
||||
let block_id = grid_builder.block_id().to_owned();
|
||||
let field_revs = grid_builder.field_revs();
|
||||
let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs);
|
||||
@ -321,6 +364,20 @@ fn make_test_grid() -> BuildGridContext {
|
||||
};
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
for field_type in FieldType::iter() {
|
||||
match field_type {
|
||||
FieldType::RichText => row_builder.insert_text_cell("AE"),
|
||||
FieldType::Number => row_builder.insert_number_cell("5"),
|
||||
FieldType::DateTime => row_builder.insert_date_cell("1671938394"),
|
||||
FieldType::SingleSelect => {
|
||||
row_builder.insert_single_select_cell(|mut options| options.remove(1))
|
||||
}
|
||||
FieldType::Checkbox => row_builder.insert_checkbox_cell("true"),
|
||||
_ => "".to_owned(),
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ impl GridGroupTest {
|
||||
let to_group = groups.get(to_group_index).unwrap();
|
||||
let to_row = to_group.rows.get(to_row_index).unwrap();
|
||||
let params = MoveGroupRowParams {
|
||||
view_id: self.inner.grid_id.clone(),
|
||||
view_id: self.inner.view_id.clone(),
|
||||
from_row_id: from_row.id.clone(),
|
||||
to_group_id: to_group.group_id.clone(),
|
||||
to_row_id: Some(to_row.id.clone()),
|
||||
|
@ -43,7 +43,7 @@ impl GridSnapshotTest {
|
||||
}
|
||||
|
||||
pub fn grid_id(&self) -> String {
|
||||
self.grid_id.clone()
|
||||
self.view_id.clone()
|
||||
}
|
||||
|
||||
pub async fn grid_pad(&self) -> GridRevisionPad {
|
||||
|
@ -1,2 +1,3 @@
|
||||
mod multi_sort_test;
|
||||
mod script;
|
||||
mod sort_test;
|
||||
mod single_sort_test;
|
||||
|
@ -0,0 +1,46 @@
|
||||
use crate::grid::sort_test::script::GridSortTest;
|
||||
use crate::grid::sort_test::script::SortScript::*;
|
||||
use flowy_grid::entities::FieldType;
|
||||
use grid_rev_model::SortCondition;
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_with_checkbox_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox).clone();
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE", "AE"],
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "No", "No", "No"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: text_field.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["", "A", "AE", "AE", "C", "DA"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
|
||||
let scripts = vec![
|
||||
InsertSort {
|
||||
field_rev: checkbox_field.clone(),
|
||||
condition: SortCondition::Descending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["", "A", "AE", "AE", "C", "DA"],
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "Yes", "No", "No"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
@ -1,23 +1,45 @@
|
||||
use crate::grid::grid_editor::GridEditorTest;
|
||||
use async_stream::stream;
|
||||
use flowy_grid::entities::{AlterSortParams, CellPathParams, DeleteSortParams};
|
||||
use grid_rev_model::SortRevision;
|
||||
use flowy_grid::services::sort::SortType;
|
||||
use flowy_grid::services::view_editor::GridViewChanged;
|
||||
use futures::stream::StreamExt;
|
||||
use grid_rev_model::{FieldRevision, SortCondition, SortRevision};
|
||||
use std::cmp::min;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::broadcast::Receiver;
|
||||
|
||||
pub enum SortScript {
|
||||
InsertSort {
|
||||
params: AlterSortParams,
|
||||
field_rev: Arc<FieldRevision>,
|
||||
condition: SortCondition,
|
||||
},
|
||||
DeleteSort {
|
||||
params: DeleteSortParams,
|
||||
field_rev: Arc<FieldRevision>,
|
||||
sort_id: String,
|
||||
},
|
||||
AssertTextOrder {
|
||||
AssertCellContentOrder {
|
||||
field_id: String,
|
||||
orders: Vec<&'static str>,
|
||||
},
|
||||
UpdateTextCell {
|
||||
row_id: String,
|
||||
text: String,
|
||||
},
|
||||
AssertSortChanged {
|
||||
old_row_orders: Vec<&'static str>,
|
||||
new_row_orders: Vec<&'static str>,
|
||||
},
|
||||
Wait {
|
||||
millis: u64,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct GridSortTest {
|
||||
inner: GridEditorTest,
|
||||
pub current_sort_rev: Option<SortRevision>,
|
||||
recv: Option<Receiver<GridViewChanged>>,
|
||||
}
|
||||
|
||||
impl GridSortTest {
|
||||
@ -26,6 +48,7 @@ impl GridSortTest {
|
||||
Self {
|
||||
inner: editor_test,
|
||||
current_sort_rev: None,
|
||||
recv: None,
|
||||
}
|
||||
}
|
||||
pub async fn run_scripts(&mut self, scripts: Vec<SortScript>) {
|
||||
@ -36,32 +59,101 @@ impl GridSortTest {
|
||||
|
||||
pub async fn run_script(&mut self, script: SortScript) {
|
||||
match script {
|
||||
SortScript::InsertSort { params } => {
|
||||
SortScript::InsertSort { condition, field_rev } => {
|
||||
self.recv = Some(self.editor.subscribe_view_changed(&self.view_id).await.unwrap());
|
||||
let params = AlterSortParams {
|
||||
view_id: self.view_id.clone(),
|
||||
field_id: field_rev.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: field_rev.ty,
|
||||
condition: condition.into(),
|
||||
};
|
||||
let sort_rev = self.editor.create_or_update_sort(params).await.unwrap();
|
||||
self.current_sort_rev = Some(sort_rev);
|
||||
}
|
||||
SortScript::DeleteSort { params } => {
|
||||
//
|
||||
SortScript::DeleteSort { field_rev, sort_id } => {
|
||||
self.recv = Some(self.editor.subscribe_view_changed(&self.view_id).await.unwrap());
|
||||
let params = DeleteSortParams {
|
||||
view_id: self.view_id.clone(),
|
||||
sort_type: SortType::from(&field_rev),
|
||||
sort_id,
|
||||
};
|
||||
self.editor.delete_sort(params).await.unwrap();
|
||||
self.current_sort_rev = None;
|
||||
}
|
||||
SortScript::AssertTextOrder { field_id, orders } => {
|
||||
SortScript::AssertCellContentOrder { field_id, orders } => {
|
||||
let mut cells = vec![];
|
||||
let rows = self.editor.get_grid(&self.grid_id).await.unwrap().rows;
|
||||
let rows = self.editor.get_grid(&self.view_id).await.unwrap().rows;
|
||||
for row in rows {
|
||||
let params = CellPathParams {
|
||||
view_id: self.grid_id.clone(),
|
||||
view_id: self.view_id.clone(),
|
||||
field_id: field_id.clone(),
|
||||
row_id: row.id,
|
||||
};
|
||||
let cell = self.editor.get_cell_display_str(¶ms).await;
|
||||
cells.push(cell);
|
||||
}
|
||||
assert_eq!(cells, orders)
|
||||
if orders.is_empty() {
|
||||
assert_eq!(cells, orders);
|
||||
} else {
|
||||
let len = min(cells.len(), orders.len());
|
||||
assert_eq!(cells.split_at(len).0, orders);
|
||||
}
|
||||
}
|
||||
SortScript::UpdateTextCell { row_id, text } => {
|
||||
self.recv = Some(self.editor.subscribe_view_changed(&self.view_id).await.unwrap());
|
||||
self.update_text_cell(row_id, &text).await;
|
||||
}
|
||||
SortScript::AssertSortChanged {
|
||||
new_row_orders,
|
||||
old_row_orders,
|
||||
} => {
|
||||
if let Some(receiver) = self.recv.take() {
|
||||
assert_sort_changed(
|
||||
receiver,
|
||||
new_row_orders.into_iter().map(|order| order.to_owned()).collect(),
|
||||
old_row_orders.into_iter().map(|order| order.to_owned()).collect(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
SortScript::Wait { millis } => {
|
||||
tokio::time::sleep(Duration::from_millis(millis)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn assert_sort_changed(
|
||||
mut receiver: Receiver<GridViewChanged>,
|
||||
new_row_orders: Vec<String>,
|
||||
old_row_orders: Vec<String>,
|
||||
) {
|
||||
let stream = stream! {
|
||||
loop {
|
||||
tokio::select! {
|
||||
changed = receiver.recv() => yield changed.unwrap(),
|
||||
_ = tokio::time::sleep(Duration::from_secs(2)) => break,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
stream
|
||||
.for_each(|changed| async {
|
||||
match changed {
|
||||
GridViewChanged::ReorderAllRowsNotification(_changed) => {}
|
||||
GridViewChanged::ReorderSingleRowNotification(changed) => {
|
||||
let mut old_row_orders = old_row_orders.clone();
|
||||
let old = old_row_orders.remove(changed.old_index);
|
||||
old_row_orders.insert(changed.new_index, old);
|
||||
assert_eq!(old_row_orders, new_row_orders);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
impl std::ops::Deref for GridSortTest {
|
||||
type Target = GridEditorTest;
|
||||
|
||||
|
@ -0,0 +1,257 @@
|
||||
use crate::grid::sort_test::script::{GridSortTest, SortScript::*};
|
||||
use flowy_grid::entities::FieldType;
|
||||
use grid_rev_model::SortCondition;
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE", "AE"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: text_field.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["", "A", "AE", "AE", "C", "DA"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_change_notification_by_update_text_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||
let scripts = vec![
|
||||
InsertSort {
|
||||
field_rev: text_field.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["", "A", "AE", "AE", "C", "DA"],
|
||||
},
|
||||
// Wait the insert task to finish. The cost of time should be less than 200 milliseconds.
|
||||
Wait { millis: 200 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
|
||||
let row_revs = test.get_row_revs().await;
|
||||
let scripts = vec![
|
||||
UpdateTextCell {
|
||||
row_id: row_revs[2].id.clone(),
|
||||
text: "E".to_string(),
|
||||
},
|
||||
AssertSortChanged {
|
||||
old_row_orders: vec!["", "A", "E", "AE", "C", "DA"],
|
||||
new_row_orders: vec!["", "A", "AE", "C", "DA", "E"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_by_ascending_and_delete_sort_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||
let scripts = vec![InsertSort {
|
||||
field_rev: text_field.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
}];
|
||||
test.run_scripts(scripts).await;
|
||||
let sort_rev = test.current_sort_rev.as_ref().unwrap();
|
||||
let scripts = vec![
|
||||
DeleteSort {
|
||||
field_rev: text_field.clone(),
|
||||
sort_id: sort_rev.id.clone(),
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE", "AE"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: text_field.clone(),
|
||||
condition: SortCondition::Descending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["DA", "C", "AE", "AE", "A", ""],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_checkbox_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "No", "No", "No"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: checkbox_field.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_checkbox_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "No", "No", "No", "Yes"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: checkbox_field.clone(),
|
||||
condition: SortCondition::Descending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "Yes", "No", "No", "No"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_date_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let date_field = test.get_first_field_rev(FieldType::DateTime);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/17", "2022/11/13"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: date_field.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/13", "2022/11/17"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_date_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let date_field = test.get_first_field_rev(FieldType::DateTime);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec![
|
||||
"2022/03/14",
|
||||
"2022/03/14",
|
||||
"2022/03/14",
|
||||
"2022/11/17",
|
||||
"2022/11/13",
|
||||
"2022/12/25",
|
||||
],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: date_field.clone(),
|
||||
condition: SortCondition::Descending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec![
|
||||
"2022/12/25",
|
||||
"2022/11/17",
|
||||
"2022/11/13",
|
||||
"2022/03/14",
|
||||
"2022/03/14",
|
||||
"2022/03/14",
|
||||
],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_number_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let number_field = test.get_first_field_rev(FieldType::Number);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: number_field.id.clone(),
|
||||
orders: vec!["$1", "$2", "$3", "$4", "", "$5"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: number_field.clone(),
|
||||
condition: SortCondition::Descending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: number_field.id.clone(),
|
||||
orders: vec!["$5", "$4", "$3", "$2", "$1", ""],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_single_select_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let single_select = test.get_first_field_rev(FieldType::SingleSelect);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: single_select.id.clone(),
|
||||
orders: vec!["", "", "Completed", "Completed", "Planned", "Planned"],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: single_select.clone(),
|
||||
condition: SortCondition::Descending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: single_select.id.clone(),
|
||||
orders: vec!["Planned", "Planned", "Completed", "Completed", "", ""],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_multi_select_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let multi_select = test.get_first_field_rev(FieldType::MultiSelect);
|
||||
let scripts = vec![
|
||||
AssertCellContentOrder {
|
||||
field_id: multi_select.id.clone(),
|
||||
orders: vec!["Google,Facebook", "Google,Twitter", "Facebook", "", "", ""],
|
||||
},
|
||||
InsertSort {
|
||||
field_rev: multi_select.clone(),
|
||||
condition: SortCondition::Ascending,
|
||||
},
|
||||
AssertCellContentOrder {
|
||||
field_id: multi_select.id.clone(),
|
||||
orders: vec!["", "", "", "Facebook", "Google,Facebook", "Google,Twitter"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
@ -1,279 +0,0 @@
|
||||
use crate::grid::sort_test::script::{GridSortTest, SortScript::*};
|
||||
use flowy_grid::entities::{AlterSortParams, DeleteSortParams, FieldType};
|
||||
use flowy_grid::services::sort::SortType;
|
||||
use grid_rev_model::SortCondition;
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: text_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::RichText.into(),
|
||||
condition: SortCondition::Ascending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["", "A", "AE", "C", "DA"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_by_ascending_and_delete_sort_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id: view_id.clone(),
|
||||
field_id: text_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::RichText.into(),
|
||||
condition: SortCondition::Ascending.into(),
|
||||
},
|
||||
}];
|
||||
test.run_scripts(scripts).await;
|
||||
let sort_rev = test.current_sort_rev.as_ref().unwrap();
|
||||
let scripts = vec![
|
||||
DeleteSort {
|
||||
params: DeleteSortParams {
|
||||
view_id,
|
||||
sort_type: SortType::from(&text_field),
|
||||
sort_id: sort_rev.id.clone(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_text_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let text_field = test.get_first_field_rev(FieldType::RichText);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["A", "", "C", "DA", "AE"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: text_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::RichText.into(),
|
||||
condition: SortCondition::Descending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: text_field.id.clone(),
|
||||
orders: vec!["DA", "C", "AE", "A", ""],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_checkbox_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "No", "No", "No"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: checkbox_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::Checkbox.into(),
|
||||
condition: SortCondition::Ascending.into(),
|
||||
},
|
||||
},
|
||||
// AssertTextOrder {
|
||||
// field_id: checkbox_field.id.clone(),
|
||||
// orders: vec!["No", "No", "No", "Yes", "Yes"],
|
||||
// },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_checkbox_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "No", "No", "No"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: checkbox_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::Checkbox.into(),
|
||||
condition: SortCondition::Descending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: checkbox_field.id.clone(),
|
||||
orders: vec!["Yes", "Yes", "No", "No", "No"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_date_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let date_field = test.get_first_field_rev(FieldType::DateTime);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/17", "2022/11/13"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: date_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::DateTime.into(),
|
||||
condition: SortCondition::Ascending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/13", "2022/11/17"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_date_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let date_field = test.get_first_field_rev(FieldType::DateTime);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/17", "2022/11/13"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: date_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::DateTime.into(),
|
||||
condition: SortCondition::Descending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: date_field.id.clone(),
|
||||
orders: vec!["2022/11/17", "2022/11/13", "2022/03/14", "2022/03/14", "2022/03/14"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_number_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let number_field = test.get_first_field_rev(FieldType::Number);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: number_field.id.clone(),
|
||||
orders: vec!["$1", "$2", "$3", "$4", ""],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: number_field.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::Number.into(),
|
||||
condition: SortCondition::Descending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: number_field.id.clone(),
|
||||
orders: vec!["$4", "$3", "$2", "$1", ""],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_single_select_by_descending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let single_select = test.get_first_field_rev(FieldType::SingleSelect);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: single_select.id.clone(),
|
||||
orders: vec!["", "", "Completed", "Completed", "Planned"],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: single_select.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::SingleSelect.into(),
|
||||
condition: SortCondition::Descending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: single_select.id.clone(),
|
||||
orders: vec!["Planned", "Completed", "Completed", "", ""],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sort_multi_select_by_ascending_test() {
|
||||
let mut test = GridSortTest::new().await;
|
||||
let multi_select = test.get_first_field_rev(FieldType::MultiSelect);
|
||||
let view_id = test.grid_id.clone();
|
||||
let scripts = vec![
|
||||
AssertTextOrder {
|
||||
field_id: multi_select.id.clone(),
|
||||
orders: vec!["Google,Facebook", "Google,Twitter", "Facebook", "", ""],
|
||||
},
|
||||
InsertSort {
|
||||
params: AlterSortParams {
|
||||
view_id,
|
||||
field_id: multi_select.id.clone(),
|
||||
sort_id: None,
|
||||
field_type: FieldType::MultiSelect.into(),
|
||||
condition: SortCondition::Ascending.into(),
|
||||
},
|
||||
},
|
||||
AssertTextOrder {
|
||||
field_id: multi_select.id.clone(),
|
||||
orders: vec!["", "", "Facebook", "Google,Facebook", "Google,Twitter"],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
@ -71,12 +71,12 @@ impl TaskDispatcher {
|
||||
Ok(result) => match result {
|
||||
Ok(_) => task.set_state(TaskState::Done),
|
||||
Err(e) => {
|
||||
tracing::error!("Process {} task failed: {:?}", handler.handler_id(), e);
|
||||
tracing::error!("Process {} task failed: {:?}", handler.handler_name(), e);
|
||||
task.set_state(TaskState::Failure);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Process {} task timeout: {:?}", handler.handler_id(), e);
|
||||
tracing::error!("Process {} task timeout: {:?}", handler.handler_name(), e);
|
||||
task.set_state(TaskState::Timeout);
|
||||
}
|
||||
}
|
||||
@ -144,6 +144,10 @@ impl TaskRunner {
|
||||
pub trait TaskHandler: Send + Sync + 'static {
|
||||
fn handler_id(&self) -> &str;
|
||||
|
||||
fn handler_name(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error>;
|
||||
}
|
||||
|
||||
@ -155,6 +159,10 @@ where
|
||||
(**self).handler_id()
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> &str {
|
||||
(**self).handler_name()
|
||||
}
|
||||
|
||||
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> {
|
||||
(**self).run(content)
|
||||
}
|
||||
@ -168,6 +176,10 @@ where
|
||||
(**self).handler_id()
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> &str {
|
||||
(**self).handler_name()
|
||||
}
|
||||
|
||||
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> {
|
||||
(**self).run(content)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user