mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-12-17 18:26:20 +00:00
fix: newly-created group rows appear out of order (#6212)
* fix: newly-created group rows appear out of order * test: fix tests * fix: dont re-insert nor insert into no-status * fix: clippy
This commit is contained in:
parent
a1793b53dd
commit
decc438b8a
@ -142,7 +142,7 @@ SPEC CHECKSUMS:
|
|||||||
HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6
|
HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6
|
||||||
hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c
|
hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c
|
||||||
irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478
|
irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478
|
||||||
local_notifier: c6c371695f914641ab7bc8601944f7e358632d0b
|
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
|
||||||
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
||||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||||
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
|
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
|
||||||
|
|||||||
@ -196,12 +196,15 @@ impl DatabaseViewEditor {
|
|||||||
let mut rows = vec![Arc::new(row.clone())];
|
let mut rows = vec![Arc::new(row.clone())];
|
||||||
self.v_filter_rows(&mut rows).await;
|
self.v_filter_rows(&mut rows).await;
|
||||||
if let Some(row) = rows.pop() {
|
if let Some(row) = rows.pop() {
|
||||||
let changesets = controller.did_create_row(&row, index);
|
if let Some(changesets) = controller.did_create_row(&row, index).await {
|
||||||
for changeset in changesets {
|
for changeset in changesets {
|
||||||
|
if !changeset.is_empty() {
|
||||||
notify_did_update_group_rows(changeset).await;
|
notify_did_update_group_rows(changeset).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.gen_did_create_row_view_tasks(index, row.clone()).await;
|
self.gen_did_create_row_view_tasks(index, row.clone()).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,7 +137,11 @@ pub trait GroupController: Send + Sync {
|
|||||||
/// Returns a changeset payload to be sent as a notification.
|
/// Returns a changeset payload to be sent as a notification.
|
||||||
///
|
///
|
||||||
/// * `row_detail`: the newly-created row
|
/// * `row_detail`: the newly-created row
|
||||||
fn did_create_row(&mut self, row: &Row, index: usize) -> Vec<GroupRowsNotificationPB>;
|
async fn did_create_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
index: usize,
|
||||||
|
) -> Option<Vec<GroupRowsNotificationPB>>;
|
||||||
|
|
||||||
/// Called after a row's cell data is changed, this moves the row to the
|
/// 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.
|
/// correct group. It may also insert a new group and/or remove an old group.
|
||||||
|
|||||||
@ -154,6 +154,57 @@ where
|
|||||||
changeset.deleted_rows.extend(deleted_row_ids);
|
changeset.deleted_rows.extend(deleted_row_ids);
|
||||||
Some(changeset)
|
Some(changeset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn insert_row_into_group(
|
||||||
|
&mut self,
|
||||||
|
new_row: &Row,
|
||||||
|
index_in_row_order_array: usize,
|
||||||
|
group_id: &str,
|
||||||
|
all_rows: &[Arc<Row>],
|
||||||
|
) -> Option<usize> {
|
||||||
|
let mut delta: i64 = 0;
|
||||||
|
let mut left_reached = false;
|
||||||
|
let mut right_reached = false;
|
||||||
|
|
||||||
|
let group_data = self.context.get_mut_group(group_id)?;
|
||||||
|
|
||||||
|
// from the index in the row order array, find the nearest row that's also in the same group
|
||||||
|
let index = loop {
|
||||||
|
delta = if delta > 0 { -delta } else { -(delta - 1) };
|
||||||
|
|
||||||
|
let query_index = index_in_row_order_array as i64 + delta;
|
||||||
|
|
||||||
|
if query_index < 0 {
|
||||||
|
if right_reached && left_reached {
|
||||||
|
break None;
|
||||||
|
} else {
|
||||||
|
left_reached = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if query_index >= all_rows.len() as i64 {
|
||||||
|
if right_reached && left_reached {
|
||||||
|
break None;
|
||||||
|
} else {
|
||||||
|
right_reached = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = &all_rows[query_index as usize];
|
||||||
|
|
||||||
|
if let Some(index) = group_data.index_of_row(&row.id.clone()) {
|
||||||
|
if delta > 0 {
|
||||||
|
break Some(index);
|
||||||
|
} else {
|
||||||
|
break Some(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
|
group_data.insert_row(index, new_row.clone());
|
||||||
|
|
||||||
|
Some(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -229,54 +280,71 @@ where
|
|||||||
self.context.move_group(from_group_id, to_group_id)
|
self.context.move_group(from_group_id, to_group_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_create_row(&mut self, row: &Row, index: usize) -> Vec<GroupRowsNotificationPB> {
|
async fn did_create_row(
|
||||||
let mut changesets: Vec<GroupRowsNotificationPB> = vec![];
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
index: usize,
|
||||||
|
) -> Option<Vec<GroupRowsNotificationPB>> {
|
||||||
let cell = match row.cells.get(&self.grouping_field_id) {
|
let cell = match row.cells.get(&self.grouping_field_id) {
|
||||||
None => self.placeholder_cell(),
|
None => self.placeholder_cell()?,
|
||||||
Some(cell) => Some(cell.clone()),
|
Some(cell) => cell.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cell) = cell {
|
let mut changesets: Vec<GroupRowsNotificationPB> = vec![];
|
||||||
|
|
||||||
let cell_data = <T as TypeOption>::CellData::from(&cell);
|
let cell_data = <T as TypeOption>::CellData::from(&cell);
|
||||||
|
|
||||||
let mut suitable_group_ids = vec![];
|
let mut did_insert_into_status_group = false;
|
||||||
|
|
||||||
for group in self.get_all_groups() {
|
let all_rows = self.delegate.get_all_rows(&self.context.view_id).await;
|
||||||
if self.can_group(&group.id, &cell_data) {
|
|
||||||
suitable_group_ids.push(group.id.clone());
|
let group_ids = self
|
||||||
let changeset = GroupRowsNotificationPB::insert(
|
.context
|
||||||
group.id.clone(),
|
.groups()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|group| group.id != self.grouping_field_id)
|
||||||
|
.map(|group| group.id.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for group_id in group_ids {
|
||||||
|
if self.can_group(&group_id, &cell_data) {
|
||||||
|
// make sure that the row isn't already in the group data
|
||||||
|
if let Some((_, group_data)) = self.get_group(&group_id) {
|
||||||
|
if group_data.contains_row(&row.id) {
|
||||||
|
did_insert_into_status_group = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(index_in_group) = self.insert_row_into_group(row, index, &group_id, &all_rows) {
|
||||||
|
did_insert_into_status_group = true;
|
||||||
|
|
||||||
|
changesets.push(GroupRowsNotificationPB::insert(
|
||||||
|
group_id.clone(),
|
||||||
vec![InsertedRowPB {
|
vec![InsertedRowPB {
|
||||||
row_meta: (*row).clone().into(),
|
row_meta: (*row).clone().into(),
|
||||||
index: Some(index as i32),
|
index: Some(index_in_group as i32),
|
||||||
is_new: true,
|
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).clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(no_status_group) = self.context.get_mut_no_status_group() {
|
|
||||||
no_status_group.add_row((*row).clone());
|
|
||||||
let changeset = GroupRowsNotificationPB::insert(
|
|
||||||
no_status_group.id.clone(),
|
|
||||||
vec![InsertedRowPB {
|
|
||||||
row_meta: (*row).clone().into(),
|
|
||||||
index: Some(index as i32),
|
|
||||||
is_new: true,
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
changesets.push(changeset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
changesets
|
if !did_insert_into_status_group {
|
||||||
|
let group_id = self.grouping_field_id.clone();
|
||||||
|
if let Some(index_in_group) = self.insert_row_into_group(row, index, &group_id, &all_rows) {
|
||||||
|
changesets.push(GroupRowsNotificationPB::insert(
|
||||||
|
self.grouping_field_id.clone(),
|
||||||
|
vec![InsertedRowPB {
|
||||||
|
row_meta: (*row).clone().into(),
|
||||||
|
index: Some(index_in_group as i32),
|
||||||
|
is_new: true,
|
||||||
|
}],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(changesets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_update_group_row(
|
fn did_update_group_row(
|
||||||
|
|||||||
@ -71,17 +71,21 @@ impl GroupController for DefaultGroupController {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_create_row(&mut self, row: &Row, index: usize) -> Vec<GroupRowsNotificationPB> {
|
async fn did_create_row(
|
||||||
self.group.add_row((*row).clone());
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
index: usize,
|
||||||
|
) -> Option<Vec<GroupRowsNotificationPB>> {
|
||||||
|
self.group.insert_row(index, row.clone());
|
||||||
|
|
||||||
vec![GroupRowsNotificationPB::insert(
|
Some(vec![GroupRowsNotificationPB::insert(
|
||||||
self.group.id.clone(),
|
self.group.id.clone(),
|
||||||
vec![InsertedRowPB {
|
vec![InsertedRowPB {
|
||||||
row_meta: (*row).clone().into(),
|
row_meta: (*row).clone().into(),
|
||||||
index: Some(index as i32),
|
index: Some(index as i32),
|
||||||
is_new: true,
|
is_new: true,
|
||||||
}],
|
}],
|
||||||
)]
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_update_group_row(
|
fn did_update_group_row(
|
||||||
|
|||||||
@ -133,15 +133,13 @@ impl GroupData {
|
|||||||
|
|
||||||
pub fn add_row(&mut self, row: Row) {
|
pub fn add_row(&mut self, row: Row) {
|
||||||
match self.rows.iter().find(|r| r.id == row.id) {
|
match self.rows.iter().find(|r| r.id == row.id) {
|
||||||
None => {
|
None => self.rows.push(row),
|
||||||
self.rows.push(row);
|
|
||||||
},
|
|
||||||
Some(_) => {},
|
Some(_) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_row(&mut self, index: usize, row: Row) {
|
pub fn insert_row(&mut self, index: usize, row: Row) {
|
||||||
if index < self.rows.len() {
|
if index <= self.rows.len() {
|
||||||
self.rows.insert(index, row);
|
self.rows.insert(index, row);
|
||||||
} else {
|
} else {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
|
|||||||
@ -3,7 +3,9 @@ use collab_database::fields::Field;
|
|||||||
use collab_database::rows::RowId;
|
use collab_database::rows::RowId;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use flowy_database2::entities::{CreateRowPayloadPB, FieldType, GroupPB, RowMetaPB};
|
use flowy_database2::entities::{
|
||||||
|
CreateRowPayloadPB, FieldType, GroupPB, OrderObjectPositionPB, RowMetaPB,
|
||||||
|
};
|
||||||
use flowy_database2::services::cell::{
|
use flowy_database2::services::cell::{
|
||||||
delete_select_option_cell, insert_date_cell, insert_select_option_cell, insert_url_cell,
|
delete_select_option_cell, insert_date_cell, insert_select_option_cell, insert_url_cell,
|
||||||
};
|
};
|
||||||
@ -36,6 +38,7 @@ pub enum GroupScript {
|
|||||||
},
|
},
|
||||||
CreateRow {
|
CreateRow {
|
||||||
group_index: usize,
|
group_index: usize,
|
||||||
|
position: OrderObjectPositionPB,
|
||||||
},
|
},
|
||||||
DeleteRow {
|
DeleteRow {
|
||||||
group_index: usize,
|
group_index: usize,
|
||||||
@ -131,16 +134,18 @@ impl DatabaseGroupTest {
|
|||||||
row_index,
|
row_index,
|
||||||
row,
|
row,
|
||||||
} => {
|
} => {
|
||||||
//
|
|
||||||
let group = self.group_at_index(group_index).await;
|
let group = self.group_at_index(group_index).await;
|
||||||
let compare_row = group.rows.get(row_index).unwrap().clone();
|
let compare_row = group.rows.get(row_index).unwrap().clone();
|
||||||
assert_eq!(row.id, compare_row.id);
|
assert_eq!(row.id, compare_row.id);
|
||||||
},
|
},
|
||||||
GroupScript::CreateRow { group_index } => {
|
GroupScript::CreateRow {
|
||||||
|
group_index,
|
||||||
|
position,
|
||||||
|
} => {
|
||||||
let group = self.group_at_index(group_index).await;
|
let group = self.group_at_index(group_index).await;
|
||||||
let params = CreateRowPayloadPB {
|
let params = CreateRowPayloadPB {
|
||||||
view_id: self.view_id.clone(),
|
view_id: self.view_id.clone(),
|
||||||
row_position: Default::default(),
|
row_position: position,
|
||||||
group_id: Some(group.group_id),
|
group_id: Some(group.group_id),
|
||||||
data: Default::default(),
|
data: Default::default(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use crate::database::group_test::script::DatabaseGroupTest;
|
use crate::database::group_test::script::DatabaseGroupTest;
|
||||||
use crate::database::group_test::script::GroupScript::*;
|
use crate::database::group_test::script::GroupScript::*;
|
||||||
use collab_database::entity::SelectOption;
|
use collab_database::entity::SelectOption;
|
||||||
|
use flowy_database2::entities::OrderObjectPositionPB;
|
||||||
|
use flowy_database2::entities::OrderObjectPositionTypePB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_init_test() {
|
async fn group_init_test() {
|
||||||
@ -201,20 +203,79 @@ async fn group_move_row_to_other_group_and_reorder_from_bottom_to_up_test() {
|
|||||||
];
|
];
|
||||||
test.run_scripts(scripts).await;
|
test.run_scripts(scripts).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_create_row_test() {
|
async fn group_create_row_test() {
|
||||||
let mut test = DatabaseGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
|
|
||||||
|
let group1 = test.group_at_index(1).await;
|
||||||
|
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateRow { group_index: 1 },
|
CreateRow {
|
||||||
|
group_index: 1,
|
||||||
|
position: Default::default(),
|
||||||
|
},
|
||||||
AssertGroupRowCount {
|
AssertGroupRowCount {
|
||||||
group_index: 1,
|
group_index: 1,
|
||||||
row_count: 3,
|
row_count: 3,
|
||||||
},
|
},
|
||||||
CreateRow { group_index: 2 },
|
AssertRow {
|
||||||
CreateRow { group_index: 2 },
|
group_index: 1,
|
||||||
|
row_index: 0,
|
||||||
|
row: group1.rows.first().unwrap().clone(),
|
||||||
|
},
|
||||||
|
AssertRow {
|
||||||
|
group_index: 1,
|
||||||
|
row_index: 1,
|
||||||
|
row: group1.rows.get(1).unwrap().clone(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
test.run_scripts(scripts).await;
|
||||||
|
|
||||||
|
let group1 = test.group_at_index(1).await;
|
||||||
|
|
||||||
|
let scripts = vec![
|
||||||
|
CreateRow {
|
||||||
|
group_index: 1,
|
||||||
|
position: OrderObjectPositionPB {
|
||||||
|
position: OrderObjectPositionTypePB::Before,
|
||||||
|
object_id: Some(group1.rows.first().unwrap().clone().id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CreateRow {
|
||||||
|
group_index: 1,
|
||||||
|
position: OrderObjectPositionPB {
|
||||||
|
position: OrderObjectPositionTypePB::After,
|
||||||
|
object_id: Some(group1.rows.first().unwrap().clone().id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CreateRow {
|
||||||
|
group_index: 1,
|
||||||
|
position: OrderObjectPositionPB {
|
||||||
|
position: OrderObjectPositionTypePB::Before,
|
||||||
|
object_id: Some(group1.rows.get(2).unwrap().clone().id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CreateRow {
|
||||||
|
group_index: 1,
|
||||||
|
position: OrderObjectPositionPB {
|
||||||
|
position: OrderObjectPositionTypePB::After,
|
||||||
|
object_id: Some(group1.rows.get(2).unwrap().clone().id),
|
||||||
|
},
|
||||||
|
},
|
||||||
AssertGroupRowCount {
|
AssertGroupRowCount {
|
||||||
group_index: 2,
|
group_index: 1,
|
||||||
row_count: 4,
|
row_count: 7,
|
||||||
|
},
|
||||||
|
AssertRow {
|
||||||
|
group_index: 1,
|
||||||
|
row_index: 1,
|
||||||
|
row: group1.rows.first().unwrap().clone(),
|
||||||
|
},
|
||||||
|
AssertRow {
|
||||||
|
group_index: 1,
|
||||||
|
row_index: 5,
|
||||||
|
row: group1.rows.get(2).unwrap().clone(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
test.run_scripts(scripts).await;
|
test.run_scripts(scripts).await;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user