chore: add board no status column

This commit is contained in:
appflowy 2022-08-30 15:21:53 +08:00
parent 03a57566f8
commit 940289c6e3
9 changed files with 212 additions and 61 deletions

View File

@ -15,6 +15,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pbserver.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../grid/application/row/row_cache.dart';
@ -154,7 +155,11 @@ class _BoardContentState extends State<BoardContent> {
}
Widget _buildFooter(BuildContext context, AFBoardColumnData columnData) {
return AppFlowyColumnFooter(
final group = columnData.customData as GroupPB;
if (group.isDefault) {
return const SizedBox();
} else {
return AppFlowyColumnFooter(
icon: SizedBox(
height: 20,
width: 20,
@ -172,7 +177,9 @@ class _BoardContentState extends State<BoardContent> {
margin: config.footerPadding,
onAddButtonClick: () {
context.read<BoardBloc>().add(BoardEvent.createRow(columnData.id));
});
},
);
}
}
Widget _buildCard(

View File

@ -81,6 +81,9 @@ pub struct GroupPB {
#[pb(index = 4)]
pub rows: Vec<RowPB>,
#[pb(index = 5)]
pub is_default: bool,
}
impl std::convert::From<Group> for GroupPB {
@ -90,6 +93,7 @@ impl std::convert::From<Group> for GroupPB {
group_id: group.id,
desc: group.name,
rows: group.rows,
is_default: group.is_default,
}
}
}

View File

@ -176,6 +176,12 @@ pub fn insert_select_option_cell(option_id: String, field_rev: &FieldRevision) -
CellRevision::new(data)
}
pub fn delete_select_option_cell(option_id: String, field_rev: &FieldRevision) -> CellRevision {
let cell_data = SelectOptionCellChangeset::from_delete(&option_id).to_str();
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
CellRevision::new(data)
}
/// If the cell data is not String type, it should impl this trait.
/// Deserialize the String into cell specific data type.
pub trait FromCellString {

View File

@ -31,6 +31,10 @@ impl<T> std::fmt::Display for GenericGroupConfiguration<T> {
self.groups_map.iter().for_each(|(_, group)| {
let _ = f.write_fmt(format_args!("Group:{} has {} rows \n", group.id, group.rows.len()));
});
let _ = f.write_fmt(format_args!(
"Default group has {} rows \n",
self.default_group.rows.len()
));
Ok(())
}
}
@ -41,6 +45,8 @@ pub struct GenericGroupConfiguration<C> {
configuration_content: PhantomData<C>,
field_rev: Arc<FieldRevision>,
groups_map: IndexMap<String, Group>,
/// default_group is used to store the rows that don't belong to any groups.
default_group: Group,
writer: Arc<dyn GroupConfigurationWriter>,
}
@ -55,6 +61,15 @@ where
reader: Arc<dyn GroupConfigurationReader>,
writer: Arc<dyn GroupConfigurationWriter>,
) -> FlowyResult<Self> {
let default_group_id = format!("{}_default_group", view_id);
let default_group = Group {
id: default_group_id,
field_id: field_rev.id.clone(),
name: format!("No {}", field_rev.name),
is_default: true,
rows: vec![],
content: "".to_string(),
};
let configuration = match reader.get_group_configuration(field_rev.clone()).await {
None => {
let default_group_configuration = default_group_configuration(&field_rev);
@ -71,6 +86,7 @@ where
view_id,
field_rev,
groups_map: IndexMap::new(),
default_group,
writer,
configuration,
configuration_content: PhantomData,
@ -82,7 +98,9 @@ where
}
pub(crate) fn clone_groups(&self) -> Vec<Group> {
self.groups_map.values().cloned().collect()
let mut groups: Vec<Group> = self.groups_map.values().cloned().collect();
groups.push(self.default_group.clone());
groups
}
pub(crate) fn merge_groups(&mut self, groups: Vec<Group>) -> FlowyResult<Option<GroupViewChangesetPB>> {
@ -160,6 +178,10 @@ where
self.groups_map.get_mut(group_id)
}
pub(crate) fn get_mut_default_group(&mut self) -> &mut Group {
&mut self.default_group
}
pub(crate) fn move_group(&mut self, from_id: &str, to_id: &str) -> FlowyResult<()> {
let from_index = self.groups_map.get_index_of(from_id);
let to_index = self.groups_map.get_index_of(to_id);

View File

@ -1,4 +1,4 @@
use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, RowPB};
use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
use crate::services::cell::{decode_any_cell_data, CellBytesParser};
use crate::services::group::action::GroupAction;
use crate::services::group::configuration::GenericGroupConfiguration;
@ -11,8 +11,6 @@ use flowy_grid_data_model::revision::{
use std::marker::PhantomData;
use std::sync::Arc;
const DEFAULT_GROUP_ID: &str = "default_group";
// Each kind of group must implement this trait to provide custom group
// operations. For example, insert cell data to the row_rev when creating
// a new row.
@ -72,8 +70,6 @@ pub struct GenericGroupController<C, T, G, P> {
pub field_id: String,
pub type_option: Option<T>,
pub configuration: GenericGroupConfiguration<C>,
/// default_group is used to store the rows that don't belong to any groups.
default_group: Group,
group_action_phantom: PhantomData<G>,
cell_parser_phantom: PhantomData<P>,
}
@ -92,22 +88,85 @@ where
let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
let _ = configuration.merge_groups(groups)?;
let default_group = Group::new(
DEFAULT_GROUP_ID.to_owned(),
field_rev.id.clone(),
format!("No {}", field_rev.name),
"".to_string(),
);
Ok(Self {
field_id: field_rev.id.clone(),
default_group,
type_option,
configuration,
group_action_phantom: PhantomData,
cell_parser_phantom: PhantomData,
})
}
fn update_default_group(
&mut self,
row_rev: &RowRevision,
other_group_changesets: &Vec<GroupChangesetPB>,
) -> GroupChangesetPB {
let default_group = self.configuration.get_mut_default_group();
// [other_group_inserted_row] contains all the inserted rows except the default group.
let other_group_inserted_row = other_group_changesets
.iter()
.flat_map(|changeset| &changeset.inserted_rows)
.collect::<Vec<&InsertedRowPB>>();
// Calculate the inserted_rows of the default_group
let default_group_inserted_row = other_group_changesets
.iter()
.flat_map(|changeset| &changeset.deleted_rows)
.cloned()
.filter(|row_id| {
// if the [other_group_inserted_row] contains the row_id of the row
// which means the row should not move to the default group.
other_group_inserted_row
.iter()
.find(|inserted_row| &inserted_row.row.id == row_id)
.is_none()
})
.collect::<Vec<String>>();
let mut changeset = GroupChangesetPB::new(default_group.id.clone());
if default_group_inserted_row.is_empty() == false {
changeset.inserted_rows.push(InsertedRowPB::new(row_rev.into()));
default_group.add_row(row_rev.into());
}
// [other_group_delete_rows] contains all the deleted rows except the default group.
let other_group_delete_rows: Vec<String> = other_group_changesets
.iter()
.flat_map(|changeset| &changeset.deleted_rows)
.cloned()
.collect();
let default_group_deleted_rows = other_group_changesets
.iter()
.flat_map(|changeset| &changeset.inserted_rows)
.filter(|inserted_row| {
// if the [other_group_delete_rows] contain the inserted_row, which means this row should move
// out from the default_group.
let inserted_row_id = &inserted_row.row.id;
other_group_delete_rows
.iter()
.find(|row_id| inserted_row_id == row_id.clone())
.is_none()
})
.collect::<Vec<&InsertedRowPB>>();
let mut deleted_row_ids = vec![];
for row in &default_group.rows {
if default_group_deleted_rows
.iter()
.find(|deleted_row| deleted_row.row.id == row.id)
.is_some()
{
deleted_row_ids.push(row.id.clone());
}
}
default_group.rows.retain(|row| !deleted_row_ids.contains(&row.id));
changeset.deleted_rows.extend(deleted_row_ids);
changeset
}
}
impl<C, T, G, P> GroupControllerSharedOperation for GenericGroupController<C, T, G, P>
@ -124,11 +183,7 @@ where
}
fn groups(&self) -> Vec<Group> {
let mut groups = self.configuration.clone_groups();
if self.default_group.is_empty() == false {
groups.insert(0, self.default_group.clone());
}
groups
self.configuration.clone_groups()
}
fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
@ -138,7 +193,6 @@ where
#[tracing::instrument(level = "trace", skip_all, fields(row_count=%row_revs.len(), group_result))]
fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>> {
// let mut ungrouped_rows = vec![];
for row_rev in row_revs {
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let mut grouped_rows: Vec<GroupedRow> = vec![];
@ -154,8 +208,7 @@ where
}
if grouped_rows.is_empty() {
// ungrouped_rows.push(RowPB::from(row_rev));
self.default_group.add_row(row_rev.into());
self.configuration.get_mut_default_group().add_row(row_rev.into());
} else {
for group_row in grouped_rows {
if let Some(group) = self.configuration.get_mut_group(&group_row.group_id) {
@ -164,30 +217,11 @@ where
}
}
} else {
self.default_group.add_row(row_rev.into());
self.configuration.get_mut_default_group().add_row(row_rev.into());
}
}
// if !ungrouped_rows.is_empty() {
// let default_group_rev = GroupRevision::default_group(gen_grid_group_id(), format!("No {}", field_rev.name));
// let default_group = Group::new(
// default_group_rev.id.clone(),
// field_rev.id.clone(),
// default_group_rev.name.clone(),
// "".to_owned(),
// );
// }
tracing::Span::current().record(
"group_result",
&format!(
"{}, default_group has {} rows",
self.configuration,
self.default_group.rows.len()
)
.as_str(),
);
tracing::Span::current().record("group_result", &format!("{},", self.configuration,).as_str());
Ok(self.groups())
}
@ -203,7 +237,12 @@ where
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
let cell_data = cell_bytes.parser::<P>()?;
let changesets = self.add_row_if_match(row_rev, &cell_data);
let mut changesets = self.add_row_if_match(row_rev, &cell_data);
let default_group_changeset = self.update_default_group(row_rev, &changesets);
tracing::info!("default_group_changeset: {}", default_group_changeset);
if !default_group_changeset.is_empty() {
changesets.push(default_group_changeset);
}
Ok(changesets)
} else {
Ok(vec![])

View File

@ -15,18 +15,25 @@ pub fn add_row(
row_rev: &RowRevision,
) -> Option<GroupChangesetPB> {
let mut changeset = GroupChangesetPB::new(group.id.clone());
cell_data.select_options.iter().for_each(|option| {
if option.id == group.id {
if !group.contains_row(&row_rev.id) {
let row_pb = RowPB::from(row_rev);
changeset.inserted_rows.push(InsertedRowPB::new(row_pb.clone()));
group.add_row(row_pb);
}
} else if group.contains_row(&row_rev.id) {
if cell_data.select_options.is_empty() {
if group.contains_row(&row_rev.id) {
changeset.deleted_rows.push(row_rev.id.clone());
group.remove_row(&row_rev.id);
}
});
} else {
cell_data.select_options.iter().for_each(|option| {
if option.id == group.id {
if !group.contains_row(&row_rev.id) {
let row_pb = RowPB::from(row_rev);
changeset.inserted_rows.push(InsertedRowPB::new(row_pb.clone()));
group.add_row(row_pb);
}
} else if group.contains_row(&row_rev.id) {
changeset.deleted_rows.push(row_rev.id.clone());
group.remove_row(&row_rev.id);
}
});
}
if changeset.is_empty() {
None

View File

@ -5,6 +5,7 @@ pub struct Group {
pub id: String,
pub field_id: String,
pub name: String,
pub is_default: bool,
pub(crate) rows: Vec<RowPB>,
/// [content] is used to determine which group the cell belongs to.
@ -16,6 +17,7 @@ impl Group {
Self {
id,
field_id,
is_default: false,
name,
rows: vec![],
content,

View File

@ -2,7 +2,7 @@ use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::{
CreateRowParams, FieldChangesetParams, FieldType, GridLayout, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB,
};
use flowy_grid::services::cell::insert_select_option_cell;
use flowy_grid::services::cell::{delete_select_option_cell, insert_select_option_cell};
use flowy_grid_data_model::revision::RowChangeset;
use std::time::Duration;
use tokio::time::interval;
@ -128,11 +128,22 @@ impl GridGroupTest {
let field_id = from_group.field_id;
let field_rev = self.editor.get_field_rev(&field_id).await.unwrap();
let field_type: FieldType = field_rev.ty.into();
let cell_rev = match field_type {
FieldType::SingleSelect => insert_select_option_cell(to_group.group_id.clone(), &field_rev),
FieldType::MultiSelect => insert_select_option_cell(to_group.group_id.clone(), &field_rev),
_ => {
panic!("Unsupported group field type");
let cell_rev = if to_group.is_default {
match field_type {
FieldType::SingleSelect => delete_select_option_cell(to_group.group_id.clone(), &field_rev),
FieldType::MultiSelect => delete_select_option_cell(to_group.group_id.clone(), &field_rev),
_ => {
panic!("Unsupported group field type");
}
}
} else {
match field_type {
FieldType::SingleSelect => insert_select_option_cell(to_group.group_id.clone(), &field_rev),
FieldType::MultiSelect => insert_select_option_cell(to_group.group_id.clone(), &field_rev),
_ => {
panic!("Unsupported group field type");
}
}
};

View File

@ -6,7 +6,7 @@ use flowy_grid::entities::FieldChangesetParams;
async fn group_init_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
AssertGroupCount(3),
AssertGroupCount(4),
AssertGroupRowCount {
group_index: 0,
row_count: 2,
@ -19,6 +19,10 @@ async fn group_init_test() {
group_index: 2,
row_count: 1,
},
AssertGroupRowCount {
group_index: 3,
row_count: 0,
},
];
test.run_scripts(scripts).await;
}
@ -294,6 +298,55 @@ async fn group_reorder_group_test() {
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn group_move_to_default_group_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
UpdateRow {
from_group_index: 0,
row_index: 0,
to_group_index: 3,
},
AssertGroupRowCount {
group_index: 0,
row_count: 1,
},
AssertGroupRowCount {
group_index: 3,
row_count: 1,
},
];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn group_move_from_default_group_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![UpdateRow {
from_group_index: 0,
row_index: 0,
to_group_index: 3,
}];
test.run_scripts(scripts).await;
let scripts = vec![
UpdateRow {
from_group_index: 3,
row_index: 0,
to_group_index: 0,
},
AssertGroupRowCount {
group_index: 0,
row_count: 2,
},
AssertGroupRowCount {
group_index: 3,
row_count: 0,
},
];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn group_move_group_test() {
let mut test = GridGroupTest::new().await;