diff --git a/frontend/appflowy_flutter/integration_test/desktop/database/database_row_test.dart b/frontend/appflowy_flutter/integration_test/desktop/database/database_row_test.dart index 93674ca52f..1982ca6e22 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/database/database_row_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/database/database_row_test.dart @@ -16,7 +16,7 @@ void main() { await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.tapCreateRowButtonInGrid(); - // The initial number of rows is 3 + // 3 initial rows + 1 created await tester.assertNumberOfRowsInGridPage(4); await tester.pumpAndSettle(); }); @@ -31,9 +31,8 @@ void main() { await tester.tapCreateRowButtonInRowMenuOfGrid(); - // The initial number of rows is 3 + // 3 initial rows + 1 created await tester.assertNumberOfRowsInGridPage(4); - await tester.assertRowCountInGridPage(4); await tester.pumpAndSettle(); }); @@ -48,9 +47,8 @@ void main() { await tester.tapRowMenuButtonInGrid(); await tester.tapDeleteOnRowMenu(); - // The initial number of rows is 3 + // 3 initial rows - 1 deleted await tester.assertNumberOfRowsInGridPage(2); - await tester.assertRowCountInGridPage(2); await tester.pumpAndSettle(); }); @@ -60,7 +58,6 @@ void main() { await tester.tapGoButton(); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); - await tester.assertRowCountInGridPage(3); await tester.pumpAndSettle(); }); diff --git a/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart b/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart index 4d6e6ecd27..50eb062c08 100644 --- a/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart +++ b/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart @@ -1,26 +1,12 @@ import 'dart:io'; -import 'package:appflowy/plugins/database/application/calculations/calculation_type_ext.dart'; -import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart'; -import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart'; -import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart'; -import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/number.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checkbox.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checklist.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/date.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/number.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/select_option.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/timestamp.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/url.dart'; -import 'package:appflowy/util/field_type_extension.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/database/application/calculations/calculation_type_ext.dart'; import 'package:appflowy/plugins/database/board/presentation/board_page.dart'; import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart'; import 'package:appflowy/plugins/database/calendar/application/calendar_bloc.dart'; @@ -30,6 +16,9 @@ import 'package:appflowy/plugins/database/calendar/presentation/calendar_event_e import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart'; import 'package:appflowy/plugins/database/calendar/presentation/toolbar/calendar_layout_setting.dart'; import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart'; @@ -43,6 +32,7 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/header/deskt import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_editor.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_list.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/date/date_time_format.dart'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/number.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/row/row.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/order_panel.dart'; @@ -52,14 +42,22 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/filt import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart'; import 'package:appflowy/plugins/database/tab_bar/desktop/tab_bar_add_button.dart'; import 'package:appflowy/plugins/database/tab_bar/desktop/tab_bar_header.dart'; -import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart'; -import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checkbox.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checklist.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/date.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/number.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/select_option.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/timestamp.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/url.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_progress_bar.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/date_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_text_field.dart'; +import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart'; +import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart'; import 'package:appflowy/plugins/database/widgets/row/row_action.dart'; import 'package:appflowy/plugins/database/widgets/row/row_banner.dart'; import 'package:appflowy/plugins/database/widgets/row/row_detail.dart'; @@ -70,6 +68,7 @@ import 'package:appflowy/plugins/database/widgets/setting/database_setting_actio import 'package:appflowy/plugins/database/widgets/setting/database_settings_list.dart'; import 'package:appflowy/plugins/database/widgets/setting/setting_button.dart'; import 'package:appflowy/plugins/database/widgets/setting/setting_property_list.dart'; +import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/clear_date_button.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/date_type_option_button.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/end_time_button.dart'; @@ -77,6 +76,7 @@ import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/remi import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_board/appflowy_board.dart'; import 'package:calendar_view/calendar_view.dart'; @@ -86,10 +86,9 @@ import 'package:flowy_infra_ui/style_widget/text_input.dart'; import 'package:flowy_infra_ui/widget/buttons/primary_button.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as p; -import 'package:table_calendar/table_calendar.dart'; - // Non-exported member of the table_calendar library import 'package:table_calendar/src/widgets/cell_content.dart'; +import 'package:table_calendar/table_calendar.dart'; import 'base.dart'; import 'common_operations.dart'; @@ -974,11 +973,6 @@ extension AppFlowyDatabaseTest on WidgetTester { await tapButtonWithName(LocaleKeys.grid_row_delete.tr()); } - Future assertRowCountInGridPage(int num) async { - final text = find.text('${rowCountString()} $num', findRichText: true); - expect(text, findsOneWidget); - } - Future createField(FieldType fieldType, String name) async { await scrollToRight(find.byType(GridPage)); await tapNewPropertyButton(); diff --git a/frontend/appflowy_flutter/lib/plugins/database/application/calculations/calculation_type_ext.dart b/frontend/appflowy_flutter/lib/plugins/database/application/calculations/calculation_type_ext.dart index 3fe2087e6b..707671d4d6 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/application/calculations/calculation_type_ext.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/application/calculations/calculation_type_ext.dart @@ -11,8 +11,22 @@ extension CalcTypeLabel on CalculationType { LocaleKeys.grid_calculationTypeLabel_median.tr(), CalculationType.Min => LocaleKeys.grid_calculationTypeLabel_min.tr(), CalculationType.Sum => LocaleKeys.grid_calculationTypeLabel_sum.tr(), + CalculationType.Count => + LocaleKeys.grid_calculationTypeLabel_count.tr(), + CalculationType.CountEmpty => + LocaleKeys.grid_calculationTypeLabel_countEmpty.tr(), + CalculationType.CountNonEmpty => + LocaleKeys.grid_calculationTypeLabel_countNonEmpty.tr(), _ => throw UnimplementedError( 'Label for $this has not been implemented', ), }; + + String get shortLabel => switch (this) { + CalculationType.CountEmpty => + LocaleKeys.grid_calculationTypeLabel_countEmptyShort.tr(), + CalculationType.CountNonEmpty => + LocaleKeys.grid_calculationTypeLabel_countNonEmptyShort.tr(), + _ => label, + }; } diff --git a/frontend/appflowy_flutter/lib/plugins/database/application/field/type_option/number_format_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/application/field/type_option/number_format_bloc.dart index a43a3be1e6..c4c31b880e 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/application/field/type_option/number_format_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/application/field/type_option/number_format_bloc.dart @@ -128,7 +128,7 @@ extension NumberFormatExtension on NumberFormatPB { } } - String iconSymbol() { + String iconSymbol([bool defaultPrefixInc = true]) { switch (this) { case NumberFormatPB.ArgentinePeso: return "\$"; @@ -169,7 +169,7 @@ extension NumberFormatExtension on NumberFormatPB { case NumberFormatPB.NorwegianKrone: return "kr"; case NumberFormatPB.Num: - return "#"; + return defaultPrefixInc ? "#" : ""; case NumberFormatPB.Percent: return "%"; case NumberFormatPB.PhilippinePeso: diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/application/calculations/field_type_calc_ext.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/application/calculations/field_type_calc_ext.dart new file mode 100644 index 0000000000..698fe0ea76 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/application/calculations/field_type_calc_ext.dart @@ -0,0 +1,34 @@ +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; + +extension AvailableCalculations on FieldType { + List calculationsForFieldType() { + final calculationTypes = [ + CalculationType.Count, + ]; + + // These FieldTypes cannot be empty, no need to count empty/non-empty + if (![FieldType.Checkbox, FieldType.LastEditedTime, FieldType.CreatedTime] + .contains(this)) { + calculationTypes.addAll([ + CalculationType.CountEmpty, + CalculationType.CountNonEmpty, + ]); + } + + switch (this) { + case FieldType.Number: + calculationTypes.addAll([ + CalculationType.Sum, + CalculationType.Average, + CalculationType.Min, + CalculationType.Max, + CalculationType.Median, + ]); + break; + default: + break; + } + + return calculationTypes; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart index bca5f06dfb..0d2a49e092 100755 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart @@ -12,7 +12,6 @@ import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart'; import 'package:flowy_infra_ui/widget/error_page.dart'; @@ -237,7 +236,6 @@ class _GridPageContentState extends State { viewId: widget.view.id, scrollController: _scrollController, ), - const _GridFooter(), ], ); } @@ -431,40 +429,3 @@ class _WrapScrollView extends StatelessWidget { ); } } - -class _GridFooter extends StatelessWidget { - const _GridFooter(); - - @override - Widget build(BuildContext context) { - return BlocSelector( - selector: (state) => state.rowCount, - builder: (context, rowCount) { - return Padding( - padding: GridSize.contentInsets, - child: RichText( - text: TextSpan( - text: rowCountString(), - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Theme.of(context).hintColor), - children: [ - TextSpan( - text: ' $rowCount', - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: AFThemeExtension.of(context).gridRowCountColor, - ), - ), - ], - ), - ), - ); - }, - ); - } -} - -String rowCountString() { - return '${LocaleKeys.grid_row_count.tr()} :'; -} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart index c39d5e68bc..9b607fc712 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart @@ -5,6 +5,7 @@ import 'package:appflowy/plugins/database/application/calculations/calculation_t import 'package:appflowy/plugins/database/application/field/field_info.dart'; import 'package:appflowy/plugins/database/application/field/type_option/number_format_bloc.dart'; import 'package:appflowy/plugins/database/grid/application/calculations/calculations_bloc.dart'; +import 'package:appflowy/plugins/database/grid/application/calculations/field_type_calc_ext.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_selector.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/remove_calculation_button.dart'; @@ -67,31 +68,29 @@ class _CalculateCellState extends State { ), ), ), - ...CalculationType.values.map( - (type) => CalculationTypeItem( - type: type, - onTap: () { - if (type != widget.calculation?.calculationType) { - context.read().add( - CalculationsEvent.updateCalculationType( - widget.fieldInfo.id, - type, - calculationId: widget.calculation?.id, - ), - ); - } - }, - ), - ), + ...widget.fieldInfo.fieldType.calculationsForFieldType().map( + (type) => CalculationTypeItem( + type: type, + onTap: () { + if (type != widget.calculation?.calculationType) { + context.read().add( + CalculationsEvent.updateCalculationType( + widget.fieldInfo.id, + type, + calculationId: widget.calculation?.id, + ), + ); + } + }, + ), + ), ], ), ); }, - child: widget.fieldInfo.fieldType == FieldType.Number - ? widget.calculation != null - ? _showCalculateValue(context, prefix) - : CalculationSelector(isSelected: isSelected) - : const SizedBox.shrink(), + child: widget.calculation != null + ? _showCalculateValue(context, prefix) + : CalculationSelector(isSelected: isSelected), ), ); } @@ -107,7 +106,7 @@ class _CalculateCellState extends State { children: [ Flexible( child: FlowyText( - widget.calculation!.calculationType.label, + widget.calculation!.calculationType.shortLabel, color: Theme.of(context).hintColor, overflow: TextOverflow.ellipsis, ), @@ -146,7 +145,7 @@ class _CalculateCellState extends State { FieldType.Number => NumberTypeOptionPB.fromBuffer(widget.fieldInfo.field.typeOptionData) .format - .iconSymbol(), + .iconSymbol(false), _ => null, }; } diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart index dba8f07709..e9ae25b96f 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart @@ -1,7 +1,8 @@ -import 'package:appflowy/plugins/database/grid/application/grid_bloc.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:appflowy/plugins/database/grid/application/grid_bloc.dart'; import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + import 'util.dart'; void main() { @@ -17,7 +18,8 @@ void main() { context = await gridTest.createTestGrid(); }); - // The initial number of rows is 3 for each grid. + // The initial number of rows is 3 for each grid + // We create one row so we expect 4 rows blocTest( "create a row", build: () => GridBloc( diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index fc537dfb91..ad5797d081 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -739,7 +739,12 @@ "max": "Max", "median": "Median", "min": "Min", - "sum": "Sum" + "sum": "Sum", + "count": "Count", + "countEmpty": "Count empty", + "countEmptyShort": "Empty", + "countNonEmpty": "Count non empty", + "countNonEmptyShort": "Not empty" } }, "document": { @@ -1350,4 +1355,4 @@ "userIcon": "User icon" }, "noLogFiles": "There're no log files" -} \ No newline at end of file +} diff --git a/frontend/rust-lib/flowy-database2/src/entities/calculation/calculation_entities.rs b/frontend/rust-lib/flowy-database2/src/entities/calculation/calculation_entities.rs index 479156bf6a..c45b567640 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/calculation/calculation_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/calculation/calculation_entities.rs @@ -6,7 +6,7 @@ use std::{ use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use crate::{impl_into_calculation_type, services::calculations::Calculation}; +use crate::{entities::FieldType, impl_into_calculation_type, services::calculations::Calculation}; #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] pub struct CalculationPB { @@ -56,10 +56,13 @@ impl std::convert::From<&Arc> for CalculationPB { pub enum CalculationType { #[default] Average = 0, // Number - Max = 1, // Number - Median = 2, // Number - Min = 3, // Number - Sum = 4, // Number + Max = 1, // Number + Median = 2, // Number + Min = 3, // Number + Sum = 4, // Number + Count = 5, // All + CountEmpty = 6, // All + CountNonEmpty = 7, // All } impl Display for CalculationType { @@ -102,6 +105,23 @@ impl From<&CalculationType> for i64 { } } +impl CalculationType { + pub fn is_allowed(&self, field_type: FieldType) -> bool { + match self { + // Number fields only + CalculationType::Max + | CalculationType::Min + | CalculationType::Average + | CalculationType::Median + | CalculationType::Sum => { + matches!(field_type, FieldType::Number) + }, + // All fields + CalculationType::Count | CalculationType::CountEmpty | CalculationType::CountNonEmpty => true, + } + } +} + #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] pub struct RepeatedCalculationsPB { #[pb(index = 1)] diff --git a/frontend/rust-lib/flowy-database2/src/entities/macros.rs b/frontend/rust-lib/flowy-database2/src/entities/macros.rs index 520e8305b5..032785dc0f 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/macros.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/macros.rs @@ -55,6 +55,9 @@ macro_rules! impl_into_calculation_type { 2 => CalculationType::Median, 3 => CalculationType::Min, 4 => CalculationType::Sum, + 5 => CalculationType::Count, + 6 => CalculationType::CountEmpty, + 7 => CalculationType::CountNonEmpty, _ => { tracing::error!("🔴 Can't parse CalculationType from value: {}", ty); CalculationType::Average diff --git a/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs b/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs index 0c78e30755..5e199b84ad 100644 --- a/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs @@ -23,6 +23,7 @@ pub trait CalculationsDelegate: Send + Sync + 'static { fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut>>; fn get_field(&self, field_id: &str) -> Option; fn get_calculation(&self, view_id: &str, field_id: &str) -> Fut>>; + fn get_all_calculations(&self, view_id: &str) -> Fut>>>; fn update_calculation(&self, view_id: &str, calculation: Calculation); fn remove_calculation(&self, view_id: &str, calculation_id: &str); } @@ -76,7 +77,7 @@ impl CalculationsController { } } - #[tracing::instrument(name = "schedule_filter_task", level = "trace", skip(self))] + #[tracing::instrument(name = "schedule_calculation_task", level = "trace", skip(self))] async fn gen_task(&self, task_type: CalculationEvent, qos: QualityOfService) { let task_id = self.task_scheduler.read().await.next_task_id(); let task = Task::new( @@ -89,10 +90,10 @@ impl CalculationsController { } #[tracing::instrument( - name = "process_filter_task", + name = "process_calculation_task", level = "trace", skip_all, - fields(filter_result), + fields(calculation_result), err )] pub async fn process(&self, predicate: &str) -> FlowyResult<()> { @@ -160,7 +161,8 @@ impl CalculationsController { .await; if let Some(calculation) = calculation { - if new_field_type != FieldType::Number { + let calc_type: CalculationType = calculation.calculation_type.into(); + if !calc_type.is_allowed(new_field_type) { self .delegate .remove_calculation(&self.view_id, &calculation.id); @@ -228,6 +230,19 @@ impl CalculationsController { let cells = row.cells.iter(); let mut updates = vec![]; + // In case there are calculations where empty cells are counted + // as a contribution to the value. + if cells.len() == 0 { + let calculations = self.delegate.get_all_calculations(&self.view_id).await; + for calculation in calculations.iter() { + let update = self.get_updated_calculation(calculation.clone()).await; + if let Some(update) = update { + updates.push(CalculationPB::from(&update)); + self.delegate.update_calculation(&self.view_id, update); + } + } + } + // Iterate each cell in the row for cell in cells { let field_id = cell.0; @@ -260,17 +275,13 @@ impl CalculationsController { .await; let field = self.delegate.get_field(&calculation.field_id)?; - if field_cells.is_empty() { - return Some(calculation.with_value(String::new())); - } else { - let value = - self - .calculations_service - .calculate(&field, calculation.calculation_type, field_cells); + let value = + self + .calculations_service + .calculate(&field, calculation.calculation_type, field_cells); - if value != calculation.value { - return Some(calculation.with_value(value)); - } + if value != calculation.value { + return Some(calculation.with_value(value)); } None diff --git a/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs b/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs index ebe517e377..dda8a68f3e 100644 --- a/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs +++ b/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs @@ -26,6 +26,9 @@ impl CalculationsService { CalculationType::Median => self.calculate_median(field, row_cells), CalculationType::Min => self.calculate_min(field, row_cells), CalculationType::Sum => self.calculate_sum(field, row_cells), + CalculationType::Count => self.calculate_count(row_cells), + CalculationType::CountEmpty => self.calculate_count_empty(row_cells), + CalculationType::CountNonEmpty => self.calculate_count_non_empty(row_cells), } } @@ -62,7 +65,7 @@ impl CalculationsService { if !values.is_empty() { format!("{:.5}", Self::median(&values)) } else { - "".to_owned() + String::new() } } @@ -89,7 +92,7 @@ impl CalculationsService { } } - "".to_owned() + String::new() } fn calculate_max(&self, field: &Field, row_cells: Vec>) -> String { @@ -105,7 +108,7 @@ impl CalculationsService { } } - "".to_owned() + String::new() } fn calculate_sum(&self, field: &Field, row_cells: Vec>) -> String { @@ -114,7 +117,45 @@ impl CalculationsService { if !values.is_empty() { format!("{:.5}", values.iter().sum::()) } else { - "".to_owned() + String::new() + } + } + + fn calculate_count(&self, row_cells: Vec>) -> String { + if !row_cells.is_empty() { + format!("{}", row_cells.len()) + } else { + String::new() + } + } + + fn calculate_count_empty(&self, row_cells: Vec>) -> String { + if !row_cells.is_empty() { + format!( + "{}", + row_cells + .iter() + .filter(|c| c.is_none()) + .collect::>() + .len() + ) + } else { + String::new() + } + } + + fn calculate_count_non_empty(&self, row_cells: Vec>) -> String { + if !row_cells.is_empty() { + format!( + "{}", + row_cells + .iter() + .filter(|c| c.is_some()) + .collect::>() + .len() + ) + } else { + String::new() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_calculations.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_calculations.rs index f329e498a4..32ddecc667 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_calculations.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_calculations.rs @@ -66,4 +66,9 @@ impl CalculationsDelegate for DatabaseViewCalculationsDelegateImpl { fn remove_calculation(&self, view_id: &str, calculation_id: &str) { self.0.remove_calculation(view_id, calculation_id) } + + fn get_all_calculations(&self, view_id: &str) -> Fut>>> { + let calculations = Arc::new(self.0.get_all_calculations(view_id)); + to_fut(async move { calculations }) + } } diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs index aefc1be4b1..61ee522a0e 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs @@ -161,14 +161,14 @@ impl DatabaseViewEditor { .payload(changes) .send(); self - .gen_did_create_row_view_tasks(row_detail.row.id.clone()) + .gen_did_create_row_view_tasks(row_detail.row.clone()) .await; } pub async fn v_did_duplicate_row(&self, row_detail: &RowDetail) { self .calculations_controller - .did_receive_row_changed(row_detail.clone().row) + .did_receive_row_changed(row_detail.row.clone()) .await; } @@ -1090,7 +1090,7 @@ impl DatabaseViewEditor { sort_controller .read() .await - .did_receive_row_changed(row_id) + .did_receive_row_changed(row_id.clone()) .await; } if let Some(calculations_controller) = weak_calculations_controller.upgrade() { @@ -1101,11 +1101,20 @@ impl DatabaseViewEditor { }); } - async fn gen_did_create_row_view_tasks(&self, row_id: RowId) { + async fn gen_did_create_row_view_tasks(&self, row: Row) { let weak_sort_controller = Arc::downgrade(&self.sort_controller); + let weak_calculations_controller = Arc::downgrade(&self.calculations_controller); af_spawn(async move { if let Some(sort_controller) = weak_sort_controller.upgrade() { - sort_controller.read().await.did_create_row(row_id).await; + sort_controller + .read() + .await + .did_create_row(row.id.clone()) + .await; + } + + if let Some(calculations_controller) = weak_calculations_controller.upgrade() { + calculations_controller.did_receive_row_changed(row).await; } }); }