feat: hidden kanban groups (#3907)

* feat: hide/unhide ui

* chore: implement collapsible side bar and adjust group header (#2)

* refactor: hidden columns into own file

* chore: adjust new group button position

* fix: flowy icon buton secondary color bleed

* chore: some UI adjustments

* fix: some regressions

* chore: proper group is_visible fetching

* chore: use a bloc to manage hidden groups

* fix: hiding groups not working

* chore: implement hidden group popups

* chore: proper ungrouped item column management

* chore: remove ungrouped items button

* chore: flowy hover build

* fix: clean up code

* test: integration tests

* fix: not null promise on null value

* fix: hide and unhide multiple groups

* chore: i18n and code review

* chore: missed review

* fix: rust-lib-test

* fix: dont completely remove flowyiconhovercolor

* chore: apply suggest

* fix: number of rows inside hidden groups not updating properly

* fix: hidden groups disappearing after collapse

* fix: hidden group title alignment

* fix: insert newly unhidden groups into the correct position

* chore: adjust padding all around

* feat: reorder hidden groups

* chore: adjust padding

* chore: collapse hidden groups section persist

* chore: no status group at beginning

* fix: hiding groups when grouping with other types

* chore: disable rename groups that arent supported

* chore: update appflowy board ref

* chore: better naming

* test: fix tests

---------

Co-authored-by: Mathias Mogensen <mathias@appflowy.io>
This commit is contained in:
Richard Shiue
2023-11-13 16:14:31 +08:00
committed by GitHub
parent 7867f0366e
commit a63a7ea611
35 changed files with 1200 additions and 749 deletions

View File

@ -6,12 +6,16 @@ use crate::services::setting::BoardLayoutSetting;
pub struct BoardLayoutSettingPB {
#[pb(index = 1)]
pub hide_ungrouped_column: bool,
#[pb(index = 2)]
pub collapse_hidden_groups: bool,
}
impl From<BoardLayoutSetting> for BoardLayoutSettingPB {
fn from(setting: BoardLayoutSetting) -> Self {
Self {
hide_ungrouped_column: setting.hide_ungrouped_column,
collapse_hidden_groups: setting.collapse_hidden_groups,
}
}
}
@ -20,6 +24,7 @@ impl From<BoardLayoutSettingPB> for BoardLayoutSetting {
fn from(setting: BoardLayoutSettingPB) -> Self {
Self {
hide_ungrouped_column: setting.hide_ungrouped_column,
collapse_hidden_groups: setting.collapse_hidden_groups,
}
}
}

View File

@ -312,7 +312,6 @@ impl DatabaseViewEditor {
.as_ref()?
.get_all_groups()
.into_iter()
.filter(|group| group.is_visible)
.map(|group_data| GroupPB::from(group_data.clone()))
.collect::<Vec<_>>();
tracing::trace!("Number of groups: {}", groups.len());
@ -398,12 +397,16 @@ impl DatabaseViewEditor {
pub async fn v_update_group(&self, changeset: GroupChangesets) -> FlowyResult<()> {
let mut type_option_data = TypeOptionData::new();
let old_field = if let Some(controller) = self.group_controller.write().await.as_mut() {
let (old_field, updated_groups) = if let Some(controller) =
self.group_controller.write().await.as_mut()
{
let old_field = self.delegate.get_field(controller.field_id());
type_option_data.extend(controller.apply_group_changeset(&changeset).await?);
old_field
let (updated_groups, new_type_option) = controller.apply_group_changeset(&changeset).await?;
type_option_data.extend(new_type_option);
(old_field, updated_groups)
} else {
None
(None, vec![])
};
if let Some(old_field) = old_field {
@ -413,6 +416,12 @@ impl DatabaseViewEditor {
.update_field(&self.view_id, type_option_data, old_field)
.await?;
}
let notification = GroupChangesPB {
view_id: self.view_id.clone(),
update_groups: updated_groups,
..Default::default()
};
notify_did_update_num_of_groups(&self.view_id, notification).await;
}
Ok(())

View File

@ -168,7 +168,7 @@ pub trait GroupControllerOperation: Send + Sync {
async fn apply_group_changeset(
&mut self,
changesets: &GroupChangesets,
) -> FlowyResult<TypeOptionData>;
) -> FlowyResult<(Vec<GroupPB>, TypeOptionData)>;
}
#[derive(Debug)]

View File

@ -173,6 +173,7 @@ where
self.field.id.clone(),
group.name.clone(),
group.id.clone(),
group.visible,
);
self.group_by_id.insert(group.id.clone(), group_data);
let (index, group_data) = self.get_group(&group.id).unwrap();
@ -338,7 +339,13 @@ where
.get(&group.id)
.cloned()
.unwrap_or_else(|| "".to_owned());
let group = GroupData::new(group.id, self.field.id.clone(), group.name, filter_content);
let group = GroupData::new(
group.id,
self.field.id.clone(),
group.name,
filter_content,
group.visible,
);
self.group_by_id.insert(group.id.clone(), group);
});
@ -351,6 +358,7 @@ where
self.field.id.clone(),
group_rev.name,
filter_content.clone(),
group_rev.visible,
);
Some(GroupPB::from(group))
})

View File

@ -11,7 +11,8 @@ use serde::Serialize;
use flowy_error::FlowyResult;
use crate::entities::{
FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB,
RowMetaPB,
};
use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser};
use crate::services::field::{default_type_option_data_from_type, TypeOption, TypeOptionCellData};
@ -45,10 +46,12 @@ pub trait GroupOperationInterceptor {
type GroupTypeOption: TypeOption;
async fn type_option_from_group_changeset(
&self,
changeset: &GroupChangeset,
type_option: &Self::GroupTypeOption,
view_id: &str,
) -> Option<TypeOptionData>;
_changeset: &GroupChangeset,
_type_option: &Self::GroupTypeOption,
_view_id: &str,
) -> Option<TypeOptionData> {
None
}
}
/// C: represents the group configuration that impl [GroupConfigurationSerde]
@ -396,7 +399,7 @@ where
async fn apply_group_changeset(
&mut self,
changeset: &GroupChangesets,
) -> FlowyResult<TypeOptionData> {
) -> FlowyResult<(Vec<GroupPB>, TypeOptionData)> {
for group_changeset in changeset.changesets.iter() {
self.context.update_group(group_changeset)?;
}
@ -410,7 +413,16 @@ where
type_option_data.extend(new_type_option_data);
}
}
Ok(type_option_data)
let updated_groups = changeset
.changesets
.iter()
.filter_map(|changeset| {
self
.get_group(&changeset.group_id)
.map(|(_, group)| GroupPB::from(group))
})
.collect::<Vec<_>>();
Ok((updated_groups, type_option_data))
}
}

View File

@ -1,5 +1,5 @@
use async_trait::async_trait;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use serde::{Deserialize, Serialize};
@ -12,8 +12,8 @@ use crate::services::group::action::GroupCustomize;
use crate::services::group::configuration::GroupContext;
use crate::services::group::controller::{BaseGroupController, GroupController};
use crate::services::group::{
move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, GroupChangeset,
GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext,
move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, GroupOperationInterceptor,
GroupsBuilder, MoveGroupRowContext,
};
#[derive(Default, Serialize, Deserialize)]
@ -190,12 +190,4 @@ pub struct CheckboxGroupOperationInterceptorImpl {}
#[async_trait]
impl GroupOperationInterceptor for CheckboxGroupOperationInterceptorImpl {
type GroupTypeOption = CheckboxTypeOption;
async fn type_option_from_group_changeset(
&self,
_changeset: &GroupChangeset,
_type_option: &Self::GroupTypeOption,
_view_id: &str,
) -> Option<TypeOptionData> {
todo!()
}
}

View File

@ -7,7 +7,7 @@ use chrono::{
};
use chrono_tz::Tz;
use collab_database::database::timestamp;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@ -24,7 +24,7 @@ use crate::services::group::configuration::GroupContext;
use crate::services::group::controller::{BaseGroupController, GroupController};
use crate::services::group::{
make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
GroupChangeset, GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext,
GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext,
};
pub trait GroupConfigurationContentSerde: Sized + Send + Sync {
@ -458,14 +458,6 @@ pub struct DateGroupOperationInterceptorImpl {}
#[async_trait]
impl GroupOperationInterceptor for DateGroupOperationInterceptorImpl {
type GroupTypeOption = DateTypeOption;
async fn type_option_from_group_changeset(
&self,
_changeset: &GroupChangeset,
_type_option: &Self::GroupTypeOption,
_view_id: &str,
) -> Option<TypeOptionData> {
todo!()
}
}
#[cfg(test)]

View File

@ -6,7 +6,9 @@ use collab_database::rows::{Cells, Row, RowDetail};
use flowy_error::FlowyResult;
use crate::entities::{GroupChangesPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB};
use crate::entities::{
GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB,
};
use crate::services::group::action::{
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation,
};
@ -30,6 +32,7 @@ impl DefaultGroupController {
field.id.clone(),
"".to_owned(),
"".to_owned(),
true,
);
Self {
field_id: field.id.clone(),
@ -129,8 +132,8 @@ impl GroupControllerOperation for DefaultGroupController {
async fn apply_group_changeset(
&mut self,
_changeset: &GroupChangesets,
) -> FlowyResult<TypeOptionData> {
Ok(TypeOptionData::default())
) -> FlowyResult<(Vec<GroupPB>, TypeOptionData)> {
Ok((Vec::new(), TypeOptionData::default()))
}
}

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use async_trait::async_trait;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use serde::{Deserialize, Serialize};
@ -17,8 +17,7 @@ use crate::services::group::configuration::GroupContext;
use crate::services::group::controller::{BaseGroupController, GroupController};
use crate::services::group::{
make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
GroupChangeset, GroupOperationInterceptor, GroupTypeOptionCellOperation, GroupsBuilder,
MoveGroupRowContext,
GroupOperationInterceptor, GroupTypeOptionCellOperation, GroupsBuilder, MoveGroupRowContext,
};
#[derive(Default, Serialize, Deserialize)]
@ -250,12 +249,4 @@ pub struct URLGroupOperationInterceptorImpl {
#[async_trait::async_trait]
impl GroupOperationInterceptor for URLGroupOperationInterceptorImpl {
type GroupTypeOption = URLTypeOption;
async fn type_option_from_group_changeset(
&self,
_changeset: &GroupChangeset,
_type_option: &Self::GroupTypeOption,
_view_id: &str,
) -> Option<TypeOptionData> {
todo!()
}
}

View File

@ -148,13 +148,19 @@ pub struct GroupData {
}
impl GroupData {
pub fn new(id: String, field_id: String, name: String, filter_content: String) -> Self {
pub fn new(
id: String,
field_id: String,
name: String,
filter_content: String,
is_visible: bool,
) -> Self {
let is_default = id == field_id;
Self {
id,
field_id,
is_default,
is_visible: true,
is_visible,
name,
rows: vec![],
filter_content,

View File

@ -93,6 +93,7 @@ pub const DEFAULT_SHOW_WEEK_NUMBERS: bool = true;
#[derive(Debug, Clone, Default)]
pub struct BoardLayoutSetting {
pub hide_ungrouped_column: bool,
pub collapse_hidden_groups: bool,
}
impl BoardLayoutSetting {
@ -107,6 +108,9 @@ impl From<LayoutSetting> for BoardLayoutSetting {
hide_ungrouped_column: setting
.get_bool_value("hide_ungrouped_column")
.unwrap_or_default(),
collapse_hidden_groups: setting
.get_bool_value("collapse_hidden_groups")
.unwrap_or_default(),
}
}
}
@ -115,6 +119,7 @@ impl From<BoardLayoutSetting> for LayoutSetting {
fn from(setting: BoardLayoutSetting) -> Self {
LayoutSettingBuilder::new()
.insert_bool_value("hide_ungrouped_column", setting.hide_ungrouped_column)
.insert_bool_value("collapse_hidden_groups", setting.collapse_hidden_groups)
.build()
}
}