mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -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)]
|
||||
|
@ -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))
|
||||
})
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ async fn board_layout_setting_test() {
|
||||
let default_board_setting = BoardLayoutSetting::new();
|
||||
let new_board_setting = BoardLayoutSetting {
|
||||
hide_ungrouped_column: true,
|
||||
..default_board_setting
|
||||
};
|
||||
let scripts = vec![
|
||||
AssertBoardLayoutSetting {
|
||||
|
Reference in New Issue
Block a user