Merge pull request #904 from AppFlowy-IO/feat/default_group_index

Feat/default group index
This commit is contained in:
Nathan.fooo 2022-08-25 14:08:07 +08:00 committed by GitHub
commit 96429e29c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 148 additions and 69 deletions

View File

@ -317,9 +317,6 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate {
@override @override
void updateRow(String groupId, RowPB row) { void updateRow(String groupId, RowPB row) {
// workaround: fix the board card reload timing issue.
Future.delayed(const Duration(milliseconds: 300), () {
controller.updateColumnItem(groupId, BoardColumnItem(row: row)); controller.updateColumnItem(groupId, BoardColumnItem(row: row));
});
} }
} }

View File

@ -68,7 +68,6 @@ class BoardSelectOptionCellState with _$BoardSelectOptionCellState {
factory BoardSelectOptionCellState.initial( factory BoardSelectOptionCellState.initial(
GridSelectOptionCellController context) { GridSelectOptionCellController context) {
final data = context.getCellData(); final data = context.getCellData();
return BoardSelectOptionCellState( return BoardSelectOptionCellState(
selectedOptions: data?.selectOptions ?? [], selectedOptions: data?.selectOptions ?? [],
); );

View File

@ -84,7 +84,6 @@ class BoardContent extends StatelessWidget {
Widget _buildHeader( Widget _buildHeader(
BuildContext context, AFBoardColumnHeaderData headerData) { BuildContext context, AFBoardColumnHeaderData headerData) {
return AppFlowyColumnHeader( return AppFlowyColumnHeader(
// icon: const Icon(Icons.lightbulb_circle),
title: Flexible( title: Flexible(
fit: FlexFit.tight, fit: FlexFit.tight,
child: FlowyText.medium( child: FlowyText.medium(
@ -95,14 +94,14 @@ class BoardContent extends StatelessWidget {
), ),
), ),
// addIcon: const Icon(Icons.add, size: 20), // addIcon: const Icon(Icons.add, size: 20),
moreIcon: SizedBox( // moreIcon: SizedBox(
width: 20, // width: 20,
height: 20, // height: 20,
child: svgWidget( // child: svgWidget(
'grid/details', // 'grid/details',
color: context.read<AppTheme>().iconColor, // color: context.read<AppTheme>().iconColor,
), // ),
), // ),
height: 50, height: 50,
margin: config.headerPadding, margin: config.headerPadding,
); );

View File

@ -5,8 +5,6 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'define.dart';
class BoardCheckboxCell extends StatefulWidget { class BoardCheckboxCell extends StatefulWidget {
final GridCellControllerBuilder cellControllerBuilder; final GridCellControllerBuilder cellControllerBuilder;
@ -41,9 +39,7 @@ class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
? svgWidget('editor/editor_check') ? svgWidget('editor/editor_check')
: svgWidget('editor/editor_uncheck'); : svgWidget('editor/editor_uncheck');
return Padding( return Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.zero,
vertical: BoardSizes.cardCellVPading,
),
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: FlowyIconButton( child: FlowyIconButton(

View File

@ -45,7 +45,7 @@ class _BoardDateCellState extends State<BoardDateCell> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Padding( child: Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
vertical: BoardSizes.cardCellVPading, vertical: BoardSizes.cardCellVPadding,
), ),
child: FlowyText.regular( child: FlowyText.regular(
state.dateStr, state.dateStr,

View File

@ -42,7 +42,7 @@ class _BoardNumberCellState extends State<BoardNumberCell> {
} else { } else {
return Padding( return Padding(
padding: padding:
EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPading), EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPadding),
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: FlowyText.medium( child: FlowyText.medium(

View File

@ -45,7 +45,8 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
) )
.toList(); .toList();
return Padding( return Padding(
padding: EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPading), padding:
EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPadding),
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: AbsorbPointer( child: AbsorbPointer(

View File

@ -41,7 +41,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Padding( child: Padding(
padding: padding:
EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPading), EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPadding),
child: FlowyText.medium( child: FlowyText.medium(
state.content, state.content,
fontSize: 14, fontSize: 14,

View File

@ -42,7 +42,7 @@ class _BoardUrlCellState extends State<BoardUrlCell> {
} else { } else {
return Padding( return Padding(
padding: padding:
EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPading), EdgeInsets.symmetric(vertical: BoardSizes.cardCellVPadding),
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: RichText( child: RichText(

View File

@ -74,6 +74,7 @@ class CardAccessoryContainer extends StatelessWidget {
width: 26, width: 26,
height: 26, height: 26,
padding: const EdgeInsets.all(3), padding: const EdgeInsets.all(3),
decoration: _makeBoxDecoration(context),
child: accessory, child: accessory,
), ),
); );
@ -88,6 +89,23 @@ class CardAccessoryContainer extends StatelessWidget {
} }
} }
BoxDecoration _makeBoxDecoration(BuildContext context) {
final theme = context.read<AppTheme>();
final borderSide = BorderSide(color: theme.shader6, width: 1.0);
return BoxDecoration(
color: theme.surface,
border: Border.fromBorderSide(borderSide),
boxShadow: [
BoxShadow(
color: theme.shader6,
spreadRadius: 0,
blurRadius: 2,
offset: Offset.zero)
],
borderRadius: const BorderRadius.all(Radius.circular(6)),
);
}
class _CardEnterRegion extends StatelessWidget { class _CardEnterRegion extends StatelessWidget {
final Widget child; final Widget child;
final List<CardAccessory> accessories; final List<CardAccessory> accessories;

View File

@ -1,3 +1,3 @@
class BoardSizes { class BoardSizes {
static double get cardCellVPading => 4; static double get cardCellVPadding => 4;
} }

View File

@ -190,7 +190,10 @@ class IGridCellController<T, D> extends Equatable {
/// cell display: $12 /// cell display: $12
_cellListener?.start(onCellChanged: (result) { _cellListener?.start(onCellChanged: (result) {
result.fold( result.fold(
(_) => _loadData(), (_) {
_cellsCache.remove(fieldId);
_loadData();
},
(err) => Log.error(err), (err) => Log.error(err),
); );
}); });

View File

@ -91,13 +91,11 @@ class SelectOptionTag extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChoiceChip( return ChoiceChip(
pressElevation: 1, pressElevation: 1,
label: Flexible( label: FlowyText.medium(
child: FlowyText.medium(
name, name,
fontSize: 12, fontSize: 12,
overflow: TextOverflow.clip, overflow: TextOverflow.clip,
), ),
),
selectedColor: color, selectedColor: color,
backgroundColor: color, backgroundColor: color,
labelPadding: const EdgeInsets.symmetric(horizontal: 6), labelPadding: const EdgeInsets.symmetric(horizontal: 6),

View File

@ -1,5 +1,5 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_grid_data_model::revision::{GroupRecordRevision, SelectOptionGroupConfigurationRevision}; use flowy_grid_data_model::revision::{GroupRevision, SelectOptionGroupConfigurationRevision};
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct UrlGroupConfigurationPB { pub struct UrlGroupConfigurationPB {
@ -36,10 +36,10 @@ pub struct GroupRecordPB {
visible: bool, visible: bool,
} }
impl std::convert::From<GroupRecordRevision> for GroupRecordPB { impl std::convert::From<GroupRevision> for GroupRecordPB {
fn from(rev: GroupRecordRevision) -> Self { fn from(rev: GroupRevision) -> Self {
Self { Self {
group_id: rev.group_id, group_id: rev.id,
visible: rev.visible, visible: rev.visible,
} }
} }

View File

@ -269,7 +269,7 @@ pub(crate) async fn create_table_row_handler(
data_result(row) data_result(row)
} }
// #[tracing::instrument(level = "debug", skip_all, err)] #[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn get_cell_handler( pub(crate) async fn get_cell_handler(
data: Data<GridCellIdPB>, data: Data<GridCellIdPB>,
manager: AppData<Arc<GridManager>>, manager: AppData<Arc<GridManager>>,

View File

@ -2,10 +2,11 @@ use crate::entities::{GroupPB, GroupViewChangesetPB, InsertedGroupPB};
use crate::services::group::{default_group_configuration, Group}; use crate::services::group::{default_group_configuration, Group};
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{ use flowy_grid_data_model::revision::{
FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRecordRevision, FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRevision,
}; };
use indexmap::IndexMap; use indexmap::IndexMap;
use lib_infra::future::AFFuture; use lib_infra::future::AFFuture;
use std::fmt::Formatter;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
@ -25,6 +26,15 @@ pub trait GroupConfigurationWriter: Send + Sync + 'static {
) -> AFFuture<FlowyResult<()>>; ) -> AFFuture<FlowyResult<()>>;
} }
impl<T> std::fmt::Display for GenericGroupConfiguration<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.groups_map.iter().for_each(|(_, group)| {
let _ = f.write_fmt(format_args!("Group:{} has {} rows \n", group.id, group.rows.len()));
});
Ok(())
}
}
pub struct GenericGroupConfiguration<C> { pub struct GenericGroupConfiguration<C> {
view_id: String, view_id: String,
pub configuration: Arc<GroupConfigurationRevision>, pub configuration: Arc<GroupConfigurationRevision>,
@ -84,12 +94,31 @@ where
let group_revs = groups let group_revs = groups
.iter() .iter()
.map(|group| GroupRecordRevision::new(group.id.clone(), group.name.clone())) .map(|group| GroupRevision::new(group.id.clone(), group.name.clone()))
.collect(); .collect::<Vec<GroupRevision>>();
self.mut_configuration(move |configuration| { self.mut_configuration(move |configuration| {
configuration.groups = group_revs; let mut is_changed = false;
true for new_group_rev in group_revs {
match configuration
.groups
.iter()
.position(|group_rev| group_rev.id == new_group_rev.id)
{
None => {
configuration.groups.push(new_group_rev);
is_changed = true;
}
Some(pos) => {
let removed_group = configuration.groups.remove(pos);
if removed_group != new_group_rev {
is_changed = true;
}
configuration.groups.insert(pos, new_group_rev);
}
}
}
is_changed
})?; })?;
groups.into_iter().for_each(|group| { groups.into_iter().for_each(|group| {
@ -139,8 +168,8 @@ where
self.groups_map.swap_indices(from_index, to_index); self.groups_map.swap_indices(from_index, to_index);
self.mut_configuration(|configuration| { self.mut_configuration(|configuration| {
let from_index = configuration.groups.iter().position(|group| group.group_id == from_id); let from_index = configuration.groups.iter().position(|group| group.id == from_id);
let to_index = configuration.groups.iter().position(|group| group.group_id == to_id); let to_index = configuration.groups.iter().position(|group| group.id == to_id);
if let (Some(from), Some(to)) = (from_index, to_index) { if let (Some(from), Some(to)) = (from_index, to_index) {
configuration.groups.swap(from, to); configuration.groups.swap(from, to);
} }
@ -183,10 +212,10 @@ where
fn mut_configuration_group( fn mut_configuration_group(
&mut self, &mut self,
group_id: &str, group_id: &str,
mut_groups_fn: impl Fn(&mut GroupRecordRevision), mut_groups_fn: impl Fn(&mut GroupRevision),
) -> FlowyResult<()> { ) -> FlowyResult<()> {
self.mut_configuration(|configuration| { self.mut_configuration(|configuration| {
match configuration.groups.iter_mut().find(|group| group.group_id == group_id) { match configuration.groups.iter_mut().find(|group| group.id == group_id) {
None => false, None => false,
Some(group_rev) => { Some(group_rev) => {
mut_groups_fn(group_rev); mut_groups_fn(group_rev);
@ -209,7 +238,7 @@ where
} }
} }
fn merge_groups(old_groups: &[GroupRecordRevision], groups: Vec<Group>) -> MergeGroupResult { fn merge_groups(old_groups: &[GroupRevision], groups: Vec<Group>) -> MergeGroupResult {
let mut merge_result = MergeGroupResult::new(); let mut merge_result = MergeGroupResult::new();
if old_groups.is_empty() { if old_groups.is_empty() {
merge_result.groups = groups; merge_result.groups = groups;
@ -224,7 +253,7 @@ fn merge_groups(old_groups: &[GroupRecordRevision], groups: Vec<Group>) -> Merge
// The group is ordered in old groups. Add them before adding the new groups // The group is ordered in old groups. Add them before adding the new groups
for group_rev in old_groups { for group_rev in old_groups {
if let Some(group) = group_map.remove(&group_rev.group_id) { if let Some(group) = group_map.remove(&group_rev.id) {
if group.name == group_rev.name { if group.name == group_rev.name {
merge_result.add_group(group); merge_result.add_group(group);
} else { } else {

View File

@ -124,7 +124,11 @@ where
} }
fn groups(&self) -> Vec<Group> { fn groups(&self) -> Vec<Group> {
self.configuration.clone_groups() let mut groups = self.configuration.clone_groups();
if self.default_group.is_empty() == false {
groups.insert(0, self.default_group.clone());
}
groups
} }
fn get_group(&self, group_id: &str) -> Option<(usize, Group)> { fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
@ -132,25 +136,28 @@ where
Some((group.0, group.1.clone())) Some((group.0, group.1.clone()))
} }
#[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>> { 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 { for row_rev in row_revs {
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let mut group_rows: Vec<GroupRow> = vec![]; let mut grouped_rows: Vec<GroupedRow> = vec![];
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
let cell_data = cell_bytes.parser::<P>()?; let cell_data = cell_bytes.parser::<P>()?;
for group in self.configuration.groups() { for group in self.configuration.groups() {
if self.can_group(&group.content, &cell_data) { if self.can_group(&group.content, &cell_data) {
group_rows.push(GroupRow { grouped_rows.push(GroupedRow {
row: row_rev.into(), row: row_rev.into(),
group_id: group.id.clone(), group_id: group.id.clone(),
}); });
} }
} }
if group_rows.is_empty() { if grouped_rows.is_empty() {
// ungrouped_rows.push(RowPB::from(row_rev));
self.default_group.add_row(row_rev.into()); self.default_group.add_row(row_rev.into());
} else { } else {
for group_row in group_rows { for group_row in grouped_rows {
if let Some(group) = self.configuration.get_mut_group(&group_row.group_id) { if let Some(group) = self.configuration.get_mut_group(&group_row.group_id) {
group.add_row(group_row.row); group.add_row(group_row.row);
} }
@ -161,13 +168,27 @@ where
} }
} }
let default_group = self.default_group.clone(); // if !ungrouped_rows.is_empty() {
let mut groups: Vec<Group> = self.configuration.clone_groups(); // let default_group_rev = GroupRevision::default_group(gen_grid_group_id(), format!("No {}", field_rev.name));
if !default_group.number_of_row() == 0 { // let default_group = Group::new(
groups.push(default_group); // default_group_rev.id.clone(),
} // field_rev.id.clone(),
// default_group_rev.name.clone(),
// "".to_owned(),
// );
// }
Ok(groups) tracing::Span::current().record(
"group_result",
&format!(
"{}, default_group has {} rows",
self.configuration,
self.default_group.rows.len()
)
.as_str(),
);
Ok(self.groups())
} }
fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> { fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
@ -222,7 +243,7 @@ where
} }
} }
struct GroupRow { struct GroupedRow {
row: RowPB, row: RowPB,
group_id: String, group_id: String,
} }

View File

@ -59,4 +59,8 @@ impl Group {
pub fn number_of_row(&self) -> usize { pub fn number_of_row(&self) -> usize {
self.rows.len() self.rows.len()
} }
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
} }

View File

@ -14,7 +14,7 @@ pub struct GroupConfigurationRevision {
pub id: String, pub id: String,
pub field_id: String, pub field_id: String,
pub field_type_rev: FieldTypeRevision, pub field_type_rev: FieldTypeRevision,
pub groups: Vec<GroupRecordRevision>, pub groups: Vec<GroupRevision>,
pub content: String, pub content: String,
} }
@ -106,24 +106,38 @@ impl GroupConfigurationContentSerde for SelectOptionGroupConfigurationRevision {
} }
} }
#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct GroupRecordRevision { pub struct GroupRevision {
pub group_id: String, pub id: String,
#[serde(default)] #[serde(default)]
pub name: String, pub name: String,
#[serde(default = "DEFAULT_GROUP_RECORD_VISIBILITY")] #[serde(skip, default = "IS_DEFAULT_GROUP")]
pub is_default: bool,
#[serde(default = "GROUP_REV_VISIBILITY")]
pub visible: bool, pub visible: bool,
} }
const DEFAULT_GROUP_RECORD_VISIBILITY: fn() -> bool = || true; const GROUP_REV_VISIBILITY: fn() -> bool = || true;
const IS_DEFAULT_GROUP: fn() -> bool = || false;
impl GroupRecordRevision { impl GroupRevision {
pub fn new(group_id: String, group_name: String) -> Self { pub fn new(id: String, group_name: String) -> Self {
Self { Self {
group_id, id,
name: group_name, name: group_name,
is_default: false,
visible: true,
}
}
pub fn default_group(id: String, group_name: String) -> Self {
Self {
id,
name: group_name,
is_default: true,
visible: true, visible: true,
} }
} }