Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: calculations #4473

Merged
merged 31 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6c3aa8a
feat: initial calculation controller
Xazin Jan 6, 2024
49ef833
chore: merge branch 'upstream/main' into feat/calculation-controller
Xazin Jan 7, 2024
97b2a10
fix: entities
Xazin Jan 18, 2024
afbd31e
chore: merge branch 'upstream/main' into feat/calculation-controller
Xazin Jan 18, 2024
89f5eaa
feat: calculations
Xazin Jan 23, 2024
366dce8
chore: merge branch 'upstream/main' into feat/calculation-controller
Xazin Jan 23, 2024
a8d8d3f
fix: review comments and support floats
Xazin Jan 24, 2024
1c7be27
fix: abstract business logic into calculations service
Xazin Jan 25, 2024
287775d
chore: merge branch 'upstream/main' into feat/calculation-controller
Xazin Jan 25, 2024
64cb71e
fix: clean calculation entities after merge
Xazin Jan 25, 2024
7c20f14
feat: react to changes to row/cell/field_type
Xazin Jan 25, 2024
7c830c9
chore: merge branch 'upstream/main' into feat/calculation-controller
Xazin Jan 25, 2024
8373f91
chore: changes after merging main
Xazin Jan 25, 2024
fc7c049
feat: handle delete field
Xazin Jan 27, 2024
4550d6d
test: add grid calculations tests
Xazin Jan 29, 2024
f5067d8
fix: add validation + format numbers
Xazin Jan 30, 2024
3dcfe1c
refactor: get cell number
appflowy Jan 30, 2024
dca29bd
Merge branch 'main' into feat/calculation-controller
appflowy Jan 30, 2024
67f28dd
chore: bump collab
appflowy Jan 30, 2024
16bbd24
chore: fix clippy
appflowy Jan 30, 2024
ef81e45
chore: update docs
appflowy Jan 30, 2024
e1f7f88
chore: update docs
appflowy Jan 30, 2024
681805e
chore: fmt
appflowy Jan 30, 2024
c61c12e
chore: fix flutter
appflowy Jan 30, 2024
05fa1ef
chore: collab rev
appflowy Jan 31, 2024
1ba6cbb
fix: cleanup and hover to show
Xazin Jan 31, 2024
d0ffa8f
chore: merge branch 'upstream/main' into feat/calculation-controller
Xazin Jan 31, 2024
fe57f5a
fix: localization
Xazin Jan 31, 2024
2967323
test: add basic rust test
Xazin Jan 31, 2024
82acdd6
fix: clippy
Xazin Jan 31, 2024
4692f54
fix: support updating calculation on duplicate row
Xazin Feb 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

// Invoke the field editor
await tester.tapGridFieldWithName('Type');
await tester.tapEditFieldButton();

await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.Checkbox);
await tester.dismissFieldEditor();
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.Checkbox);

await tester.assertFieldTypeWithFieldName(
'Type',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import '../util/database_test_op.dart';
import '../util/util.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

group('Grid Calculations', () {
testWidgets('add calculation and update cell', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();

await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

// Change one Field to Number
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.Number);

expect(find.text('Calculate'), findsOneWidget);

await tester.changeCalculateAtIndex(1, CalculationType.Sum);

// Enter values in cells
await tester.editCell(
rowIndex: 0,
fieldType: FieldType.Number,
input: '100',
);

await tester.editCell(
rowIndex: 1,
fieldType: FieldType.Number,
input: '100',
);

// Dismiss edit cell
await tester.sendKeyDownEvent(LogicalKeyboardKey.enter);

await tester.pumpAndSettle(const Duration(seconds: 1));

expect(find.text('200'), findsOneWidget);
});

testWidgets('add calculations and remove row', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();

await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

// Change two Fields to Number
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.Number);
await tester.changeFieldTypeOfFieldWithName('Done', FieldType.Number);

expect(find.text('Calculate'), findsNWidgets(2));

await tester.changeCalculateAtIndex(1, CalculationType.Sum);
await tester.changeCalculateAtIndex(2, CalculationType.Min);

// Enter values in cells
await tester.editCell(
rowIndex: 0,
fieldType: FieldType.Number,
input: '100',
);
await tester.editCell(
rowIndex: 1,
fieldType: FieldType.Number,
input: '150',
);
await tester.editCell(
rowIndex: 0,
fieldType: FieldType.Number,
input: '50',
cellIndex: 1,
);
await tester.editCell(
rowIndex: 1,
fieldType: FieldType.Number,
input: '100',
cellIndex: 1,
);

await tester.pumpAndSettle();

// Dismiss edit cell
await tester.sendKeyDownEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();

expect(find.text('250'), findsOneWidget);
expect(find.text('50'), findsNWidgets(2));

// Delete 1st row
await tester.hoverOnFirstRowOfGrid();
await tester.tapRowMenuButtonInGrid();
await tester.tapDeleteOnRowMenu();

await tester.pumpAndSettle(const Duration(seconds: 1));

expect(find.text('150'), findsNWidgets(2));
expect(find.text('100'), findsNWidgets(2));
});
});
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
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';
Expand Down Expand Up @@ -717,6 +720,31 @@ extension AppFlowyDatabaseTest on WidgetTester {
await pumpAndSettle();
}

Future<void> changeFieldTypeOfFieldWithName(
String name,
FieldType type,
) async {
await tapGridFieldWithName(name);
await tapEditFieldButton();

await tapSwitchFieldTypeButton();
await selectFieldType(type);
await dismissFieldEditor();
}

Future<void> changeCalculateAtIndex(int index, CalculationType type) async {
await tap(find.byType(CalculateCell).at(index));
await pumpAndSettle();

await tap(
find.descendant(
of: find.byType(CalculationTypeItem),
matching: find.text(type.label),
),
);
await pumpAndSettle();
}

/// Should call [tapGridFieldWithName] first.
Future<void> tapEditFieldButton() async {
await tapButtonWithName(LocaleKeys.grid_field_editProperty.tr());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';

extension CalcTypeLabel on CalculationType {
String get label => switch (this) {
CalculationType.Average =>
LocaleKeys.grid_calculationTypeLabel_average.tr(),
CalculationType.Max => LocaleKeys.grid_calculationTypeLabel_max.tr(),
CalculationType.Median =>
LocaleKeys.grid_calculationTypeLabel_median.tr(),
CalculationType.Min => LocaleKeys.grid_calculationTypeLabel_min.tr(),
CalculationType.Sum => LocaleKeys.grid_calculationTypeLabel_sum.tr(),
_ => throw UnimplementedError(
'Label for $this has not been implemented',
),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'dart:async';
import 'dart:typed_data';

import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.dart';

typedef UpdateCalculationValue
= Either<CalculationChangesetNotificationPB, FlowyError>;

class CalculationsListener {
CalculationsListener({required this.viewId});

final String viewId;

PublishNotifier<UpdateCalculationValue>? _calculationNotifier =
PublishNotifier();
DatabaseNotificationListener? _listener;

void start({
required void Function(UpdateCalculationValue) onCalculationChanged,
}) {
_calculationNotifier?.addPublishListener(onCalculationChanged);
_listener = DatabaseNotificationListener(
objectId: viewId,
handler: _handler,
);
}

void _handler(
DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case DatabaseNotification.DidUpdateCalculation:
_calculationNotifier?.value = result.fold(
(payload) => left(
CalculationChangesetNotificationPB.fromBuffer(payload),
),
(err) => right(err),
);
default:
break;
}
}

Future<void> stop() async {
await _listener?.stop();
_calculationNotifier?.dispose();
_calculationNotifier = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';

class CalculationsBackendService {
const CalculationsBackendService({required this.viewId});

final String viewId;

// Get Calculations (initial fetch)
Future<Either<RepeatedCalculationsPB, FlowyError>> getCalculations() async {
final payload = DatabaseViewIdPB()..value = viewId;

return DatabaseEventGetAllCalculations(payload).send();
}

Future<void> updateCalculation(
String fieldId,
CalculationType type, {
String? calculationId,
}) async {
final payload = UpdateCalculationChangesetPB()
..viewId = viewId
..fieldId = fieldId
..calculationType = type;

if (calculationId != null) {
payload.calculationId = calculationId;
}

await DatabaseEventUpdateCalculation(payload).send();
}

Future<void> removeCalculation(
String fieldId,
String calculationId,
) async {
final payload = RemoveCalculationChangesetPB()
..viewId = viewId
..fieldId = fieldId
..calculationId = calculationId;

await DatabaseEventRemoveCalculation(payload).send();
}
}
Loading
Loading