feat: add new group (#3854)

* feat: implement backend logic

* fix: did_create_row not working properly

* fix: did_delete_group not working properly

* fix: test

* chore: fix clippy

* fix: new card not editable and in wrong position

* feat: imlement UI for add new stack

* test: add integration test

* chore: i18n

* chore: remove debug message

* chore: merge conflict

---------

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Richard Shiue
2023-11-06 16:17:05 +08:00
committed by GitHub
parent 4d82bb5322
commit c4fc60612f
28 changed files with 674 additions and 138 deletions

View File

@ -197,3 +197,36 @@ impl From<UpdateGroupParams> for GroupChangeset {
}
}
}
#[derive(Debug, Default, ProtoBuf)]
pub struct CreateGroupPayloadPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2)]
pub group_config_id: String,
#[pb(index = 3)]
pub name: String,
}
#[derive(Debug, Clone)]
pub struct CreateGroupParams {
pub view_id: String,
pub group_config_id: String,
pub name: String,
}
impl TryFrom<CreateGroupPayloadPB> for CreateGroupParams {
type Error = ErrorCode;
fn try_from(value: CreateGroupPayloadPB) -> Result<Self, Self::Error> {
let view_id = NotEmptyStr::parse(value.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
let name = NotEmptyStr::parse(value.name).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
Ok(CreateGroupParams {
view_id: view_id.0,
group_config_id: value.group_config_id,
name: name.0,
})
}
}

View File

@ -741,6 +741,20 @@ pub(crate) async fn move_group_row_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(manager), err)]
pub(crate) async fn create_group_handler(
data: AFPluginData<CreateGroupPayloadPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> FlowyResult<()> {
let manager = upgrade_manager(manager)?;
let params: CreateGroupParams = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
database_editor
.create_group(&params.view_id, &params.name)
.await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(manager), err)]
pub(crate) async fn get_databases_handler(
manager: AFPluginState<Weak<DatabaseManager>>,

View File

@ -60,6 +60,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
.event(DatabaseEvent::GetGroups, get_groups_handler)
.event(DatabaseEvent::GetGroup, get_group_handler)
.event(DatabaseEvent::UpdateGroup, update_group_handler)
.event(DatabaseEvent::CreateGroup, create_group_handler)
// Database
.event(DatabaseEvent::GetDatabases, get_databases_handler)
// Calendar
@ -284,6 +285,9 @@ pub enum DatabaseEvent {
#[event(input = "UpdateGroupPB")]
UpdateGroup = 113,
#[event(input = "CreateGroupPayloadPB")]
CreateGroup = 114,
/// Returns all the databases
#[event(output = "RepeatedDatabaseDescriptionPB")]
GetDatabases = 120,

View File

@ -442,7 +442,7 @@ impl DatabaseEditor {
let row_detail = self.database.lock().get_row_detail(&row_order.id);
if let Some(row_detail) = row_detail {
for view in self.database_views.editors().await {
view.v_did_create_row(&row_detail, &group_id, index).await;
view.v_did_create_row(&row_detail, index).await;
}
return Ok(Some(row_detail));
}
@ -961,6 +961,12 @@ impl DatabaseEditor {
Ok(())
}
pub async fn create_group(&self, view_id: &str, name: &str) -> FlowyResult<()> {
let view_editor = self.database_views.get_view_editor(view_id).await?;
view_editor.v_create_group(name).await?;
Ok(())
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn set_layout_setting(
&self,

View File

@ -14,8 +14,8 @@ use lib_dispatch::prelude::af_spawn;
use crate::entities::{
CalendarEventPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, DeleteFilterParams,
DeleteGroupParams, DeleteSortParams, FieldType, FieldVisibility, GroupChangesPB, GroupPB,
GroupRowsNotificationPB, InsertedRowPB, LayoutSettingChangeset, LayoutSettingParams, RowMetaPB,
RowsChangePB, SortChangesetNotificationPB, SortPB, UpdateFilterParams, UpdateSortParams,
InsertedRowPB, LayoutSettingChangeset, LayoutSettingParams, RowMetaPB, RowsChangePB,
SortChangesetNotificationPB, SortPB, UpdateFilterParams, UpdateSortParams,
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::cell::CellCache;
@ -126,39 +126,22 @@ impl DatabaseViewEditor {
.send();
}
pub async fn v_did_create_row(
&self,
row_detail: &RowDetail,
group_id: &Option<String>,
index: usize,
) {
let changes: RowsChangePB;
pub async fn v_did_create_row(&self, row_detail: &RowDetail, index: usize) {
// Send the group notification if the current view has groups
match group_id.as_ref() {
None => {
let row = InsertedRowPB::new(RowMetaPB::from(row_detail)).with_index(index as i32);
changes = RowsChangePB::from_insert(row);
},
Some(group_id) => {
self
.mut_group_controller(|group_controller, _| {
group_controller.did_create_row(row_detail, group_id);
Ok(())
})
.await;
if let Some(controller) = self.group_controller.write().await.as_mut() {
let changesets = controller.did_create_row(row_detail, index);
let inserted_row = InsertedRowPB {
row_meta: RowMetaPB::from(row_detail),
index: Some(index as i32),
is_new: true,
};
let changeset =
GroupRowsNotificationPB::insert(group_id.clone(), vec![inserted_row.clone()]);
for changeset in changesets {
notify_did_update_group_rows(changeset).await;
changes = RowsChangePB::from_insert(inserted_row);
},
}
}
let inserted_row = InsertedRowPB {
row_meta: RowMetaPB::from(row_detail),
index: Some(index as i32),
is_new: true,
};
let changes = RowsChangePB::from_insert(inserted_row);
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changes)
.send();
@ -168,16 +151,22 @@ impl DatabaseViewEditor {
pub async fn v_did_delete_row(&self, row: &Row) {
// Send the group notification if the current view has groups;
let result = self
.mut_group_controller(|group_controller, field| {
group_controller.did_delete_delete_row(row, &field)
})
.mut_group_controller(|group_controller, _| group_controller.did_delete_row(row))
.await;
if let Some(result) = result {
tracing::trace!("Delete row in view changeset: {:?}", result.row_changesets);
tracing::trace!("Delete row in view changeset: {:?}", result);
for changeset in result.row_changesets {
notify_did_update_group_rows(changeset).await;
}
if let Some(deleted_group) = result.deleted_group {
let payload = GroupChangesPB {
view_id: self.view_id.clone(),
deleted_groups: vec![deleted_group.group_id],
..Default::default()
};
notify_did_update_num_of_groups(&self.view_id, payload).await;
}
}
let changes = RowsChangePB::from_delete(row.id.clone().into_inner());
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
@ -319,7 +308,7 @@ impl DatabaseViewEditor {
.read()
.await
.as_ref()?
.groups()
.get_all_groups()
.into_iter()
.filter(|group| group.is_visible)
.map(|group_data| GroupPB::from(group_data.clone()))
@ -371,6 +360,36 @@ impl DatabaseViewEditor {
Ok(())
}
pub async fn v_create_group(&self, name: &str) -> FlowyResult<()> {
let mut old_field: Option<Field> = None;
let result = if let Some(controller) = self.group_controller.write().await.as_mut() {
let create_group_results = controller.create_group(name.to_string())?;
old_field = self.delegate.get_field(controller.field_id());
create_group_results
} else {
(None, None)
};
if let Some(old_field) = old_field {
if let (Some(type_option_data), Some(payload)) = result {
self
.delegate
.update_field(&self.view_id, type_option_data, old_field)
.await?;
let group_changes = GroupChangesPB {
view_id: self.view_id.clone(),
inserted_groups: vec![payload],
..Default::default()
};
notify_did_update_num_of_groups(&self.view_id, group_changes).await;
}
}
Ok(())
}
pub async fn v_delete_group(&self, _params: DeleteGroupParams) -> FlowyResult<()> {
Ok(())
}
@ -671,7 +690,7 @@ impl DatabaseViewEditor {
.await?;
let new_groups = new_group_controller
.groups()
.get_all_groups()
.into_iter()
.map(|group| GroupPB::from(group.clone()))
.collect();

View File

@ -25,15 +25,27 @@ pub trait SelectTypeOptionSharedAction: Send + Sync {
/// If the option already exists, it will be updated.
/// If the option does not exist, it will be inserted at the beginning.
fn insert_option(&mut self, new_option: SelectOption) {
self.insert_option_at_index(new_option, None);
}
fn insert_option_at_index(&mut self, new_option: SelectOption, new_index: Option<usize>) {
let options = self.mut_options();
let safe_new_index = new_index.map(|index| {
if index > options.len() {
options.len()
} else {
index
}
});
if let Some(index) = options
.iter()
.position(|option| option.id == new_option.id || option.name == new_option.name)
{
options.remove(index);
options.insert(index, new_option);
options.insert(safe_new_index.unwrap_or(index), new_option);
} else {
options.insert(0, new_option);
options.insert(safe_new_index.unwrap_or(0), new_option);
}
}

View File

@ -54,7 +54,7 @@ pub trait GroupCustomize: Send + Sync {
&mut self,
row: &Row,
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> Vec<GroupRowsNotificationPB>;
) -> (Option<GroupPB>, Vec<GroupRowsNotificationPB>);
/// Move row from one group to another
fn move_row(
@ -71,27 +71,71 @@ pub trait GroupCustomize: Send + Sync {
) -> Option<GroupPB> {
None
}
fn generate_new_group(
&mut self,
_name: String,
) -> FlowyResult<(Option<TypeOptionData>, Option<InsertedGroupPB>)> {
Ok((None, None))
}
}
/// Defines the shared actions any group controller can perform.
#[async_trait]
pub trait GroupControllerOperation: Send + Sync {
/// The field that is used for grouping the rows
/// Returns the id of field that is being used to group the rows
fn field_id(&self) -> &str;
/// Returns number of groups the current field has
fn groups(&self) -> Vec<&GroupData>;
/// Returns all of the groups currently managed by the controller
fn get_all_groups(&self) -> Vec<&GroupData>;
/// Returns the index and the group data with group_id
/// Returns the index and the group data with the given group id if it exists.
///
/// * `group_id` - A string slice that is used to match the group
fn get_group(&self, group_id: &str) -> Option<(usize, GroupData)>;
/// Separates the rows into different groups
/// Sort the rows into the different groups.
///
/// * `rows`: rows to be inserted
/// * `field`: reference to the field being sorted (currently unused)
fn fill_groups(&mut self, rows: &[&RowDetail], field: &Field) -> FlowyResult<()>;
/// Remove the group with from_group_id and insert it to the index with to_group_id
/// Create a new group, currently only supports single and multi-select.
///
/// Returns a new type option data for the grouping field if it's altered.
///
/// * `name`: name of the new group
fn create_group(
&mut self,
name: String,
) -> FlowyResult<(Option<TypeOptionData>, Option<InsertedGroupPB>)>;
/// Reorders the group in the group controller.
///
/// * `from_group_id`: id of the group being moved
/// * `to_group_id`: id of the group whose index is the one at which the
/// reordered group will be placed
fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
/// Insert/Remove the row to the group if the corresponding cell data is changed
/// Adds a newly-created row to one or more suitable groups.
///
/// Returns a changeset payload to be sent as a notification.
///
/// * `row_detail`: the newly-created row
fn did_create_row(
&mut self,
row_detail: &RowDetail,
index: usize,
) -> Vec<GroupRowsNotificationPB>;
/// Called after a row's cell data is changed, this moves the row to the
/// correct group. It may also insert a new group and/or remove an old group.
///
/// Returns the inserted and removed groups if necessary for notification.
///
/// * `old_row_detail`:
/// * `row_detail`:
/// * `field`:
fn did_update_group_row(
&mut self,
old_row_detail: &Option<RowDetail>,
@ -99,22 +143,31 @@ pub trait GroupControllerOperation: Send + Sync {
field: &Field,
) -> FlowyResult<DidUpdateGroupRowResult>;
/// Remove the row from the group if the row gets deleted
fn did_delete_delete_row(
&mut self,
row: &Row,
field: &Field,
) -> FlowyResult<DidMoveGroupRowResult>;
/// Called after the row is deleted, this removes the row from the group.
/// A group could be deleted as a result.
///
/// Returns a the removed group when this occurs.
fn did_delete_row(&mut self, row: &Row) -> FlowyResult<DidMoveGroupRowResult>;
/// Move the row from one group to another group
/// Reorders a row within the current group or move the row to another group.
///
/// * `context`: information about the row being moved and its destination
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<DidMoveGroupRowResult>;
/// Update the group if the corresponding field is changed
/// Updates the groups after a field change. (currently never does anything)
///
/// * `field`: new changeset
fn did_update_group_field(&mut self, field: &Field) -> FlowyResult<Option<GroupChangesPB>>;
/// Updates the name and/or visibility of groups.
///
/// Returns a non-empty `TypeOptionData` when the changes require a change
/// in the field type option data.
///
/// * `changesets`: list of changesets to be made to one or more groups
async fn apply_group_changeset(
&mut self,
changeset: &GroupChangesets,
changesets: &GroupChangesets,
) -> FlowyResult<TypeOptionData>;
}

View File

@ -124,6 +124,7 @@ where
/// Returns the no `status` group
///
/// We take the `id` of the `field` as the no status group id
#[allow(dead_code)]
pub(crate) fn get_no_status_group(&self) -> Option<&GroupData> {
self.group_by_id.get(&self.field.id)
}
@ -249,7 +250,7 @@ where
///
/// # Arguments
///
/// * `generated_group_configs`: the generated groups contains a list of [GeneratedGroupConfig].
/// * `generated_groups`: the generated groups contains a list of [GeneratedGroupConfig].
///
/// Each [FieldType] can implement the [GroupGenerator] trait in order to generate different
/// groups. For example, the FieldType::Checkbox has the [CheckboxGroupGenerator] that implements

View File

@ -11,7 +11,7 @@ use serde::Serialize;
use flowy_error::FlowyResult;
use crate::entities::{
FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB,
FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
};
use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser};
use crate::services::field::{default_type_option_data_from_type, TypeOption, TypeOptionCellData};
@ -38,9 +38,6 @@ pub trait GroupController: GroupControllerOperation + Send + Sync {
/// Called before the row was created.
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str);
/// Called after the row was created.
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str);
}
#[async_trait]
@ -184,7 +181,7 @@ where
&self.grouping_field_id
}
fn groups(&self) -> Vec<&GroupData> {
fn get_all_groups(&self) -> Vec<&GroupData> {
self.context.groups()
}
@ -233,10 +230,70 @@ where
Ok(())
}
fn create_group(
&mut self,
name: String,
) -> FlowyResult<(Option<TypeOptionData>, Option<InsertedGroupPB>)> {
self.generate_new_group(name)
}
fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
self.context.move_group(from_group_id, to_group_id)
}
fn did_create_row(
&mut self,
row_detail: &RowDetail,
index: usize,
) -> Vec<GroupRowsNotificationPB> {
let cell = match row_detail.row.cells.get(&self.grouping_field_id) {
None => self.placeholder_cell(),
Some(cell) => Some(cell.clone()),
};
let mut changesets: Vec<GroupRowsNotificationPB> = vec![];
if let Some(cell) = cell {
let cell_data = <T as TypeOption>::CellData::from(&cell);
let mut suitable_group_ids = vec![];
for group in self.get_all_groups() {
if self.can_group(&group.filter_content, &cell_data) {
suitable_group_ids.push(group.id.clone());
let changeset = GroupRowsNotificationPB::insert(
group.id.clone(),
vec![InsertedRowPB {
row_meta: row_detail.into(),
index: Some(index as i32),
is_new: true,
}],
);
changesets.push(changeset);
}
}
if !suitable_group_ids.is_empty() {
for group_id in suitable_group_ids.iter() {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone());
}
}
} else if let Some(no_status_group) = self.context.get_mut_no_status_group() {
no_status_group.add_row(row_detail.clone());
let changeset = GroupRowsNotificationPB::insert(
no_status_group.id.clone(),
vec![InsertedRowPB {
row_meta: row_detail.into(),
index: Some(index as i32),
is_new: true,
}],
);
changesets.push(changeset);
}
}
changesets
}
fn did_update_group_row(
&mut self,
old_row_detail: &Option<RowDetail>,
@ -278,26 +335,21 @@ where
Ok(result)
}
fn did_delete_delete_row(
&mut self,
row: &Row,
_field: &Field,
) -> FlowyResult<DidMoveGroupRowResult> {
// if the cell_rev is none, then the row must in the default group.
fn did_delete_row(&mut self, row: &Row) -> FlowyResult<DidMoveGroupRowResult> {
let mut result = DidMoveGroupRowResult {
deleted_group: None,
row_changesets: vec![],
};
// early return if the row is not in the default group
if let Some(cell) = row.cells.get(&self.grouping_field_id) {
let cell_data = <T as TypeOption>::CellData::from(cell);
if !cell_data.is_cell_empty() {
tracing::error!("did_delete_delete_row {:?}", cell);
result.row_changesets = self.delete_row(row, &cell_data);
(result.deleted_group, result.row_changesets) = self.delete_row(row, &cell_data);
return Ok(result);
}
}
match self.context.get_no_status_group() {
match self.context.get_mut_no_status_group() {
None => {
tracing::error!("Unexpected None value. It should have the no status group");
},
@ -305,6 +357,7 @@ where
if !no_status_group.contains_row(&row.id) {
tracing::error!("The row: {:?} should be in the no status group", row.id);
}
no_status_group.remove_row(&row.id);
result.row_changesets = vec![GroupRowsNotificationPB::delete(
no_status_group.id.clone(),
vec![row.id.clone().into_inner()],

View File

@ -3,7 +3,7 @@ use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB};
use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB};
use crate::services::cell::insert_checkbox_cell;
use crate::services::field::{
CheckboxCellDataParser, CheckboxTypeOption, TypeOption, CHECK, UNCHECK,
@ -109,7 +109,7 @@ impl GroupCustomize for CheckboxGroupController {
&mut self,
row: &Row,
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> Vec<GroupRowsNotificationPB> {
) -> (Option<GroupPB>, Vec<GroupRowsNotificationPB>) {
let mut changesets = vec![];
self.context.iter_mut_groups(|group| {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
@ -122,7 +122,7 @@ impl GroupCustomize for CheckboxGroupController {
changesets.push(changeset);
}
});
changesets
(None, changesets)
}
fn move_row(
@ -155,12 +155,6 @@ impl GroupController for CheckboxGroupController {
},
}
}
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone())
}
}
}
pub struct CheckboxGroupBuilder();

View File

@ -178,8 +178,8 @@ impl GroupCustomize for DateGroupController {
fn delete_row(
&mut self,
row: &Row,
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> Vec<GroupRowsNotificationPB> {
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> (Option<GroupPB>, Vec<GroupRowsNotificationPB>) {
let mut changesets = vec![];
self.context.iter_mut_groups(|group| {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
@ -192,7 +192,23 @@ impl GroupCustomize for DateGroupController {
changesets.push(changeset);
}
});
changesets
let setting_content = self.context.get_setting_content();
let deleted_group =
match self
.context
.get_group(&group_id(cell_data, &self.type_option, &setting_content))
{
Some((_, group)) if group.rows.len() == 1 => Some(group.clone()),
_ => None,
};
let deleted_group = deleted_group.map(|group| {
let _ = self.context.delete_group(&group.id);
group.into()
});
(deleted_group, changesets)
}
fn move_row(
@ -247,12 +263,6 @@ impl GroupController for DateGroupController {
},
}
}
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone())
}
}
}
pub struct DateGroupBuilder();

View File

@ -6,7 +6,7 @@ use collab_database::rows::{Cells, Row, RowDetail};
use flowy_error::FlowyResult;
use crate::entities::GroupChangesPB;
use crate::entities::{GroupChangesPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB};
use crate::services::group::action::{
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation,
};
@ -44,7 +44,7 @@ impl GroupControllerOperation for DefaultGroupController {
&self.field_id
}
fn groups(&self) -> Vec<&GroupData> {
fn get_all_groups(&self) -> Vec<&GroupData> {
vec![&self.group]
}
@ -59,10 +59,34 @@ impl GroupControllerOperation for DefaultGroupController {
Ok(())
}
fn create_group(
&mut self,
_name: String,
) -> FlowyResult<(Option<TypeOptionData>, Option<InsertedGroupPB>)> {
Ok((None, None))
}
fn move_group(&mut self, _from_group_id: &str, _to_group_id: &str) -> FlowyResult<()> {
Ok(())
}
fn did_create_row(
&mut self,
row_detail: &RowDetail,
index: usize,
) -> Vec<GroupRowsNotificationPB> {
self.group.add_row(row_detail.clone());
vec![GroupRowsNotificationPB::insert(
self.group.id.clone(),
vec![InsertedRowPB {
row_meta: row_detail.into(),
index: Some(index as i32),
is_new: true,
}],
)]
}
fn did_update_group_row(
&mut self,
_old_row_detail: &Option<RowDetail>,
@ -76,14 +100,15 @@ impl GroupControllerOperation for DefaultGroupController {
})
}
fn did_delete_delete_row(
&mut self,
_row: &Row,
_field: &Field,
) -> FlowyResult<DidMoveGroupRowResult> {
fn did_delete_row(&mut self, row: &Row) -> FlowyResult<DidMoveGroupRowResult> {
let mut changeset = GroupRowsNotificationPB::new(self.group.id.clone());
if self.group.contains_row(&row.id) {
self.group.remove_row(&row.id);
changeset.deleted_rows.push(row.id.clone().into_inner());
}
Ok(DidMoveGroupRowResult {
deleted_group: None,
row_changesets: vec![],
row_changesets: vec![changeset],
})
}
@ -115,6 +140,4 @@ impl GroupController for DefaultGroupController {
}
fn will_create_row(&mut self, _cells: &mut Cells, _field: &Field, _group_id: &str) {}
fn did_create_row(&mut self, _row_detail: &RowDetail, _group_id: &str) {}
}

View File

@ -1,9 +1,10 @@
use async_trait::async_trait;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use flowy_error::FlowyResult;
use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupRowsNotificationPB};
use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::field::{
MultiSelectTypeOption, SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction,
@ -13,7 +14,7 @@ use crate::services::group::action::GroupCustomize;
use crate::services::group::controller::{BaseGroupController, GroupController};
use crate::services::group::{
add_or_remove_select_option_row, generate_select_option_groups, make_no_status_group,
move_group_row, remove_select_option_row, GeneratedGroups, GroupChangeset, GroupContext,
move_group_row, remove_select_option_row, GeneratedGroups, Group, GroupChangeset, GroupContext,
GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext,
};
@ -69,14 +70,14 @@ impl GroupCustomize for MultiSelectGroupController {
&mut self,
row: &Row,
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> Vec<GroupRowsNotificationPB> {
) -> (Option<GroupPB>, Vec<GroupRowsNotificationPB>) {
let mut changesets = vec![];
self.context.iter_mut_status_groups(|group| {
if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
changesets.push(changeset);
}
});
changesets
(None, changesets)
}
fn move_row(
@ -92,6 +93,20 @@ impl GroupCustomize for MultiSelectGroupController {
});
group_changeset
}
fn generate_new_group(
&mut self,
name: String,
) -> FlowyResult<(Option<TypeOptionData>, Option<InsertedGroupPB>)> {
let mut new_type_option = self.type_option.clone();
let new_select_option = self.type_option.create_option(&name);
new_type_option.insert_option(new_select_option.clone());
let new_group = Group::new(new_select_option.id, new_select_option.name);
let inserted_group_pb = self.context.add_new_group(new_group)?;
Ok((Some(new_type_option.into()), Some(inserted_group_pb)))
}
}
impl GroupController for MultiSelectGroupController {
@ -106,12 +121,6 @@ impl GroupController for MultiSelectGroupController {
},
}
}
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone())
}
}
}
pub struct MultiSelectGroupBuilder;

View File

@ -1,9 +1,10 @@
use async_trait::async_trait;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use flowy_error::FlowyResult;
use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupRowsNotificationPB};
use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::field::{
SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, SingleSelectTypeOption,
@ -14,8 +15,8 @@ use crate::services::group::controller::{BaseGroupController, GroupController};
use crate::services::group::controller_impls::select_option_controller::util::*;
use crate::services::group::entities::GroupData;
use crate::services::group::{
make_no_status_group, GeneratedGroups, GroupChangeset, GroupContext, GroupOperationInterceptor,
GroupsBuilder, MoveGroupRowContext,
make_no_status_group, GeneratedGroups, Group, GroupChangeset, GroupContext,
GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext,
};
#[derive(Default, Serialize, Deserialize)]
@ -70,14 +71,14 @@ impl GroupCustomize for SingleSelectGroupController {
&mut self,
row: &Row,
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> Vec<GroupRowsNotificationPB> {
) -> (Option<GroupPB>, Vec<GroupRowsNotificationPB>) {
let mut changesets = vec![];
self.context.iter_mut_status_groups(|group| {
if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
changesets.push(changeset);
}
});
changesets
(None, changesets)
}
fn move_row(
@ -93,6 +94,23 @@ impl GroupCustomize for SingleSelectGroupController {
});
group_changeset
}
fn generate_new_group(
&mut self,
name: String,
) -> FlowyResult<(Option<TypeOptionData>, Option<InsertedGroupPB>)> {
let mut new_type_option = self.type_option.clone();
let new_select_option = self.type_option.create_option(&name);
new_type_option.insert_option_at_index(
new_select_option.clone(),
Some(new_type_option.options.len()),
);
let new_group = Group::new(new_select_option.id, new_select_option.name);
let inserted_group_pb = self.context.add_new_group(new_group)?;
Ok((Some(new_type_option.into()), Some(inserted_group_pb)))
}
}
impl GroupController for SingleSelectGroupController {
@ -108,12 +126,6 @@ impl GroupController for SingleSelectGroupController {
},
}
}
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone())
}
}
}
pub struct SingleSelectGroupBuilder();

View File

@ -185,6 +185,7 @@ pub fn make_inserted_cell(group_id: &str, field: &Field) -> Option<Cell> {
},
}
}
pub fn generate_select_option_groups(
_field_id: &str,
options: &[SelectOption],

View File

@ -128,8 +128,8 @@ impl GroupCustomize for URLGroupController {
fn delete_row(
&mut self,
row: &Row,
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> Vec<GroupRowsNotificationPB> {
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
) -> (Option<GroupPB>, Vec<GroupRowsNotificationPB>) {
let mut changesets = vec![];
self.context.iter_mut_groups(|group| {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
@ -142,7 +142,18 @@ impl GroupCustomize for URLGroupController {
changesets.push(changeset);
}
});
changesets
let deleted_group = match self.context.get_group(&cell_data.data) {
Some((_, group)) if group.rows.len() == 1 => Some(group.clone()),
_ => None,
};
let deleted_group = deleted_group.map(|group| {
let _ = self.context.delete_group(&group.id);
group.into()
});
(deleted_group, changesets)
}
fn move_row(
@ -190,12 +201,6 @@ impl GroupController for URLGroupController {
},
}
}
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone())
}
}
}
pub struct URLGroupGenerator();