From 3b0d82287d8fe2c9e92fa36ff68b80b23203a75e Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Tue, 5 Mar 2024 21:45:39 +0800 Subject: [PATCH 01/13] fix: some mobile ui bugs (#4823) * fix: select option editor mobile * chore: field type icon --- .../card/card_detail/widgets/option_text_field.dart | 5 +++-- .../cell_editor/mobile_select_option_editor.dart | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart index 3439ba3afa..fc869a54c7 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart @@ -42,12 +42,13 @@ class OptionTextField extends StatelessWidget { width: 38, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), - color: type.mobileIconBackgroundColor, + color: Theme.of(context).brightness == Brightness.light + ? type.mobileIconBackgroundColor + : type.mobileIconBackgroundColorDark, ), child: Center( child: FlowySvg( type.svgData, - blendMode: null, size: const Size.square(22), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell_editor/mobile_select_option_editor.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell_editor/mobile_select_option_editor.dart index f7c1c6d67d..f1c8b424f5 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell_editor/mobile_select_option_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell_editor/mobile_select_option_editor.dart @@ -84,12 +84,11 @@ class _MobileSelectOptionEditorState extends State { const height = 44.0; return Stack( children: [ - Align( - alignment: Alignment.centerLeft, - child: showMoreOptions - ? AppBarBackButton(onTap: _popOrBack) - : AppBarCloseButton(onTap: _popOrBack), - ), + if (showMoreOptions) + Align( + alignment: Alignment.centerLeft, + child: AppBarBackButton(onTap: _popOrBack), + ), SizedBox( height: 44.0, child: Align( From 66aea29ab78975ce7c8fae7124ea20c2373762cf Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Tue, 5 Mar 2024 19:16:56 +0100 Subject: [PATCH 02/13] feat: generic calculations (#4794) * feat: add generic calculations * chore: remove row count at bottom of grid * fix: code review --- .../desktop/database/database_row_test.dart | 9 ++-- .../shared/database_test_op.dart | 42 +++++++--------- .../calculations/calculation_type_ext.dart | 14 ++++++ .../field/type_option/number_format_bloc.dart | 4 +- .../calculations/field_type_calc_ext.dart | 34 +++++++++++++ .../database/grid/presentation/grid_page.dart | 39 --------------- .../widgets/calculations/calculate_cell.dart | 45 +++++++++-------- .../bloc_test/grid_test/grid_bloc_test.dart | 8 +-- frontend/resources/translations/en.json | 9 +++- .../calculation/calculation_entities.rs | 30 ++++++++++-- .../flowy-database2/src/entities/macros.rs | 3 ++ .../src/services/calculations/controller.rs | 39 +++++++++------ .../src/services/calculations/service.rs | 49 +++++++++++++++++-- .../database_view/view_calculations.rs | 5 ++ .../src/services/database_view/view_editor.rs | 19 +++++-- 15 files changed, 222 insertions(+), 127 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/database/grid/application/calculations/field_type_calc_ext.dart 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; } }); } From 6e2caf3358e8fc344af8f6e43e1ca5825cdf1aae Mon Sep 17 00:00:00 2001 From: Zack Date: Wed, 6 Mar 2024 17:27:52 +0800 Subject: [PATCH 03/13] fix: delete workspace after deleting from remote (#4830) --- .../user/af_cloud_test/workspace_test.rs | 21 +++++++++++++++++++ .../user_manager/manager_user_workspace.rs | 14 +++++++++++++ 2 files changed, 35 insertions(+) diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs index 802349c512..176a39aa9a 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs @@ -7,6 +7,27 @@ use flowy_user::protobuf::UserNotification; use crate::util::receive_with_timeout; +#[tokio::test] +async fn af_cloud_workspace_delete() { + user_localhost_af_cloud().await; + let test = EventIntegrationTest::new().await; + let user_profile_pb = test.af_cloud_sign_up().await; + let workspaces = get_synced_workspaces(&test, user_profile_pb.id).await; + assert_eq!(workspaces.len(), 1); + + let created_workspace = test.create_workspace("my second workspace").await; + assert_eq!(created_workspace.name, "my second workspace"); + let workspaces = get_synced_workspaces(&test, user_profile_pb.id).await; + assert_eq!(workspaces.len(), 2); + + test.delete_workspace(&created_workspace.workspace_id).await; + let workspaces = get_synced_workspaces(&test, user_profile_pb.id).await; + assert_eq!(workspaces.len(), 1); + + let workspaces = test.get_all_workspaces().await.items; + assert_eq!(workspaces.len(), 1); +} + #[tokio::test] async fn af_cloud_workspace_name_change() { user_localhost_af_cloud().await; diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index 1b3ea81533..dfed83ccfe 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -188,6 +188,9 @@ impl UserManager { .get_user_service()? .delete_workspace(workspace_id) .await?; + let uid = self.user_id()?; + let conn = self.db_connection(uid)?; + delete_user_workspaces(conn, workspace_id)?; Ok(()) } @@ -325,3 +328,14 @@ pub fn save_user_workspaces( Ok::<(), FlowyError>(()) }) } + +pub fn delete_user_workspaces(mut conn: DBConnection, workspace_id: &str) -> FlowyResult<()> { + let n = conn.immediate_transaction(|conn| { + let rows_affected: usize = + diesel::delete(user_workspace_table::table.filter(user_workspace_table::id.eq(workspace_id))) + .execute(conn)?; + Ok::(rows_affected) + })?; + assert_eq!(n, 1); + Ok(()) +} From f5cb8b6d250a1c6c4e2e477d5e51808c1e3822db Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:31:30 +0100 Subject: [PATCH 04/13] fix: image url check for embed link (#4826) * fix: image url check in for embed link * chore: move all patterns to shared * test: prefer enterText over manipulating widget --- .../shared/editor_test_operations.dart | 8 +++--- .../setting/font/font_picker_screen.dart | 23 +++++++--------- .../widgets/calculations/calculate_cell.dart | 6 ++--- .../copy_and_paste/paste_from_plain_text.dart | 9 +++---- .../image/embed_image_url_widget.dart | 26 ++++++++++++++++--- .../mobile_toolbar_v3/_font_item.dart | 3 ++- .../lib/shared/patterns/common_patterns.dart | 23 ++++++++++++++++ .../shared/patterns/date_time_patterns.dart | 19 ++++++++++++++ .../util/google_font_family_extension.dart | 9 +++---- .../lib/util/string_extension.dart | 8 +++--- .../settings/application_data_storage.dart | 6 +++-- .../settings/date_time/time_patterns.dart | 18 ------------- .../font_family_setting.dart | 21 +++++---------- 13 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart create mode 100644 frontend/appflowy_flutter/lib/shared/patterns/date_time_patterns.dart delete mode 100644 frontend/appflowy_flutter/lib/workspace/application/settings/date_time/time_patterns.dart diff --git a/frontend/appflowy_flutter/integration_test/shared/editor_test_operations.dart b/frontend/appflowy_flutter/integration_test/shared/editor_test_operations.dart index 64dbd1746e..786b9e08ce 100644 --- a/frontend/appflowy_flutter/integration_test/shared/editor_test_operations.dart +++ b/frontend/appflowy_flutter/integration_test/shared/editor_test_operations.dart @@ -1,6 +1,9 @@ import 'dart:async'; import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/base/emoji/emoji_picker.dart'; import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart'; @@ -14,8 +17,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embe import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_emoji_mart/flutter_emoji_mart.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -132,8 +133,7 @@ class EditorOperations { of: find.byType(EmbedImageUrlWidget), matching: find.byType(TextField), ); - final textField = tester.widget(imageUrlTextField); - textField.controller?.text = imageUrl; + await tester.enterText(imageUrlTextField, imageUrl); await tester.pumpAndSettle(); await tester.tapButton( find.descendant( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart index a2a6ca04f5..f79bc3dd78 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart @@ -1,10 +1,12 @@ +import 'package:flutter/material.dart'; + import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/base/app_bar.dart'; import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_search_text_field.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; +import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -23,9 +25,7 @@ class FontPickerScreen extends StatelessWidget { } class LanguagePickerPage extends StatefulWidget { - const LanguagePickerPage({ - super.key, - }); + const LanguagePickerPage({super.key}); @override State createState() => _LanguagePickerPageState(); @@ -52,6 +52,7 @@ class _LanguagePickerPageState extends State { body: SafeArea( child: Scrollbar( child: ListView.builder( + itemCount: availableFonts.length + 1, // with search bar itemBuilder: (context, index) { if (index == 0) { // search bar @@ -65,7 +66,8 @@ class _LanguagePickerPageState extends State { setState(() { availableFonts = _availableFonts .where( - (element) => parseFontFamilyName(element) + (font) => font + .parseFontFamilyName() .toLowerCase() .contains(keyword.toLowerCase()), ) @@ -75,8 +77,9 @@ class _LanguagePickerPageState extends State { ), ); } + final fontFamilyName = availableFonts[index - 1]; - final displayName = parseFontFamilyName(fontFamilyName); + final displayName = fontFamilyName.parseFontFamilyName(); return FlowyOptionTile.checkbox( text: displayName, isSelected: selectedFontFamilyName == fontFamilyName, @@ -86,17 +89,9 @@ class _LanguagePickerPageState extends State { backgroundColor: Colors.transparent, ); }, - itemCount: availableFonts.length + 1, // with search bar ), ), ), ); } - - String parseFontFamilyName(String fontFamilyName) { - final camelCase = RegExp('(?<=[a-z])[A-Z]'); - return fontFamilyName - .replaceAll('_regular', '') - .replaceAllMapped(camelCase, (m) => ' ${m.group(0)}'); - } } 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 9b607fc712..2e73e72e12 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 @@ -9,6 +9,7 @@ import 'package:appflowy/plugins/database/grid/application/calculations/field_ty 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'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/calculation_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pb.dart'; @@ -132,9 +133,8 @@ class _CalculateCellState extends State { } String _withoutTrailingZeros(String value) { - final regex = RegExp(r'^(\d+(?:\.\d*?[1-9](?=0|\b))?)\.?0*$'); - if (regex.hasMatch(value)) { - final match = regex.firstMatch(value)!; + if (trailingZerosRegex.hasMatch(value)) { + final match = trailingZerosRegex.firstMatch(value)!; return match.group(1)!; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_plain_text.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_plain_text.dart index 7f31309fc6..114dde62a3 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_plain_text.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_plain_text.dart @@ -1,10 +1,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/editor_state_paste_node_extension.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; -RegExp _hrefRegex = RegExp( - r'https?://(?:www\.)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(?:/[^\s]*)?', -); - extension PasteFromPlainText on EditorState { Future pastePlainText(String plainText) async { if (await pasteHtmlIfAvailable(plainText)) { @@ -23,7 +20,7 @@ extension PasteFromPlainText on EditorState { .map((e) { // parse the url content final Attributes attributes = {}; - if (_hrefRegex.hasMatch(e)) { + if (hrefRegex.hasMatch(e)) { attributes[AppFlowyRichTextKeys.href] = e; } return Delta()..insert(e, attributes: attributes); @@ -45,7 +42,7 @@ extension PasteFromPlainText on EditorState { if (selection == null || !selection.isSingle || selection.isCollapsed || - !_hrefRegex.hasMatch(plainText)) { + !hrefRegex.hasMatch(plainText)) { return false; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart index 34400597ac..d34a4fc3a8 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart @@ -1,7 +1,9 @@ +import 'package:flutter/material.dart'; + import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; class EmbedImageUrlWidget extends StatefulWidget { const EmbedImageUrlWidget({ @@ -16,6 +18,7 @@ class EmbedImageUrlWidget extends StatefulWidget { } class _EmbedImageUrlWidgetState extends State { + bool isUrlValid = true; String inputText = ''; @override @@ -25,8 +28,15 @@ class _EmbedImageUrlWidgetState extends State { FlowyTextField( hintText: LocaleKeys.document_imageBlock_embedLink_placeholder.tr(), onChanged: (value) => inputText = value, - onEditingComplete: () => widget.onSubmit(inputText), + onEditingComplete: submit, ), + if (!isUrlValid) ...[ + const VSpace(8), + FlowyText( + LocaleKeys.document_plugins_cover_invalidImageUrl.tr(), + color: Theme.of(context).colorScheme.error, + ), + ], const VSpace(8), SizedBox( width: 160, @@ -37,10 +47,20 @@ class _EmbedImageUrlWidgetState extends State { LocaleKeys.document_imageBlock_embedLink_label.tr(), textAlign: TextAlign.center, ), - onTap: () => widget.onSubmit(inputText), + onTap: submit, ), ), ], ); } + + void submit() { + if (checkUrlValidity(inputText)) { + return widget.onSubmit(inputText); + } + + setState(() => isUrlValid = false); + } + + bool checkUrlValidity(String url) => imgUrlRegex.hasMatch(url); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart index cc5914f53b..13384f9b87 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart @@ -1,12 +1,13 @@ import 'dart:async'; +import 'package:flutter/material.dart'; + import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; diff --git a/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart b/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart new file mode 100644 index 0000000000..fce937908a --- /dev/null +++ b/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart @@ -0,0 +1,23 @@ +const _trailingZerosPattern = r'^(\d+(?:\.\d*?[1-9](?=0|\b))?)\.?0*$'; +final trailingZerosRegex = RegExp(_trailingZerosPattern); + +const _hrefPattern = + r'https?://(?:www\.)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(?:/[^\s]*)?'; +final hrefRegex = RegExp(_hrefPattern); + +/// This pattern allows for both HTTP and HTTPS Scheme +/// It allows for query parameters +/// It only allows the following image extensions: .png, .jpg, .gif, .webm +/// +const _imgUrlPattern = + r'(https?:\/\/)([^\s(["<,>/]*)(\/)[^\s[",><]*(.png|.jpg|.gif|.webm)(\?[^\s[",><]*)?'; +final imgUrlRegex = RegExp(_imgUrlPattern); + +const _appflowyCloudUrlPattern = r'^(https:\/\/)(.*)(\.appflowy\.cloud\/)(.*)'; +final appflowyCloudUrlRegex = RegExp(_appflowyCloudUrlPattern); + +const _camelCasePattern = '(?<=[a-z])[A-Z]'; +final camelCaseRegex = RegExp(_camelCasePattern); + +const _macOSVolumesPattern = '^/Volumes/[^/]+'; +final macOSVolumesRegex = RegExp(_macOSVolumesPattern); diff --git a/frontend/appflowy_flutter/lib/shared/patterns/date_time_patterns.dart b/frontend/appflowy_flutter/lib/shared/patterns/date_time_patterns.dart new file mode 100644 index 0000000000..530e9d4558 --- /dev/null +++ b/frontend/appflowy_flutter/lib/shared/patterns/date_time_patterns.dart @@ -0,0 +1,19 @@ +/// RegExp to match Twelve Hour formats +/// Source: https://stackoverflow.com/a/33906224 +/// +/// Matches eg: "05:05 PM", "5:50 Pm", "10:59 am", etc. +/// +const _twelveHourTimePattern = + r'\b((1[0-2]|0?[1-9]):([0-5][0-9]) ([AaPp][Mm]))'; +final twelveHourTimeRegex = RegExp(_twelveHourTimePattern); +bool isTwelveHourTime(String? time) => twelveHourTimeRegex.hasMatch(time ?? ''); + +/// RegExp to match Twenty Four Hour formats +/// Source: https://stackoverflow.com/a/7536768 +/// +/// Matches eg: "0:01", "04:59", "16:30", etc. +/// +const _twentyFourHourtimePattern = r'^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$'; +final tewentyFourHourTimeRegex = RegExp(_twentyFourHourtimePattern); +bool isTwentyFourHourTime(String? time) => + tewentyFourHourTimeRegex.hasMatch(time ?? ''); diff --git a/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart b/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart index d20a42fc87..30a3229085 100644 --- a/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart +++ b/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart @@ -1,7 +1,6 @@ +import 'package:appflowy/shared/patterns/common_patterns.dart'; + extension GoogleFontsParser on String { - String parseFontFamilyName() { - final camelCase = RegExp('(?<=[a-z])[A-Z]'); - return replaceAll('_regular', '') - .replaceAllMapped(camelCase, (m) => ' ${m.group(0)}'); - } + String parseFontFamilyName() => replaceAll('_regular', '') + .replaceAllMapped(camelCaseRegex, (m) => ' ${m.group(0)}'); } diff --git a/frontend/appflowy_flutter/lib/util/string_extension.dart b/frontend/appflowy_flutter/lib/util/string_extension.dart index 0730a9f76d..3db25f7885 100644 --- a/frontend/appflowy_flutter/lib/util/string_extension.dart +++ b/frontend/appflowy_flutter/lib/util/string_extension.dart @@ -2,6 +2,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; + extension StringExtension on String { static const _specialCharacters = r'\/:*?"<>| '; @@ -31,8 +33,6 @@ extension StringExtension on String { return null; } - /// Returns if the string is a appflowy cloud url. - bool get isAppFlowyCloudUrl { - return RegExp(r'^(https:\/\/)(.*)(\.appflowy\.cloud\/)(.*)').hasMatch(this); - } + /// Returns true if the string is a appflowy cloud url. + bool get isAppFlowyCloudUrl => appflowyCloudUrlRegex.hasMatch(this); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart index 99a3b77d58..b0c8cb0948 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart @@ -1,10 +1,12 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; + import 'package:appflowy/core/config/kv.dart'; import 'package:appflowy/core/config/kv_keys.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy_backend/log.dart'; -import 'package:flutter/foundation.dart'; import 'package:path/path.dart' as p; import '../../../startup/tasks/prelude.dart'; @@ -26,7 +28,7 @@ class ApplicationDataStorage { if (Platform.isMacOS) { // remove the prefix `/Volumes/*` - path = path.replaceFirst(RegExp('^/Volumes/[^/]+'), ''); + path = path.replaceFirst(macOSVolumesRegex, ''); } else if (Platform.isWindows) { path = path.replaceAll('/', '\\'); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/date_time/time_patterns.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/date_time/time_patterns.dart deleted file mode 100644 index 57443b23f8..0000000000 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/date_time/time_patterns.dart +++ /dev/null @@ -1,18 +0,0 @@ -/// RegExp to match Twelve Hour formats -/// Source: https://stackoverflow.com/a/33906224 -/// -/// Matches eg: "05:05 PM", "5:50 Pm", "10:59 am", etc. -/// -final _twelveHourTimePattern = - RegExp(r'\b((1[0-2]|0?[1-9]):([0-5][0-9]) ([AaPp][Mm]))'); -bool isTwelveHourTime(String? time) => - _twelveHourTimePattern.hasMatch(time ?? ''); - -/// RegExp to match Twenty Four Hour formats -/// Source: https://stackoverflow.com/a/7536768 -/// -/// Matches eg: "0:01", "04:59", "16:30", etc. -/// -final _twentyFourHourtimePattern = RegExp(r'^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$'); -bool isTwentyFourHourTime(String? time) => - _twentyFourHourtimePattern.hasMatch(time ?? ''); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart index 922316fcbe..7a25e2f032 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart @@ -95,7 +95,7 @@ class _FontFamilyDropDownState extends State { return FlowySettingValueDropDown( popoverKey: ThemeFontFamilySetting.popoverKey, popoverController: widget.popoverController, - currentValue: parseFontFamilyName(widget.currentFontFamily), + currentValue: widget.currentFontFamily.parseFontFamilyName(), onClose: () { query.value = ''; widget.onClose?.call(); @@ -162,18 +162,11 @@ class _FontFamilyDropDownState extends State { ); } - String parseFontFamilyName(String fontFamilyName) { - final camelCase = RegExp('(?<=[a-z])[A-Z]'); - return fontFamilyName - .replaceAll('_regular', '') - .replaceAllMapped(camelCase, (m) => ' ${m.group(0)}'); - } - Widget _fontFamilyItemButton( BuildContext context, TextStyle style, ) { - final buttonFontFamily = parseFontFamilyName(style.fontFamily!); + final buttonFontFamily = style.fontFamily!.parseFontFamilyName(); return Tooltip( message: buttonFontFamily, @@ -184,21 +177,19 @@ class _FontFamilyDropDownState extends State { child: FlowyButton( onHover: (_) => FocusScope.of(context).unfocus(), text: FlowyText.medium( - parseFontFamilyName(style.fontFamily!), + buttonFontFamily, fontFamily: style.fontFamily!, ), rightIcon: - buttonFontFamily == parseFontFamilyName(widget.currentFontFamily) - ? const FlowySvg( - FlowySvgs.check_s, - ) + buttonFontFamily == widget.currentFontFamily.parseFontFamilyName() + ? const FlowySvg(FlowySvgs.check_s) : null, onTap: () { if (widget.onFontFamilyChanged != null) { widget.onFontFamilyChanged!(style.fontFamily!); } else { final fontFamily = style.fontFamily!.parseFontFamilyName(); - if (parseFontFamilyName(widget.currentFontFamily) != + if (widget.currentFontFamily.parseFontFamilyName() != buttonFontFamily) { context .read() From f4846750083db1ad79840f0d36a49471dc252b7e Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 7 Mar 2024 09:28:31 +0800 Subject: [PATCH 05/13] fix: uninitialized error in members setting page (#4834) --- .../home/menu/sidebar/workspace/_sidebar_workspace_menu.dart | 3 +-- .../settings/widgets/members/workspace_member_bloc.dart | 2 ++ .../settings/widgets/members/workspace_member_page.dart | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart index c02b2ad22e..b2ab95638e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart @@ -113,8 +113,7 @@ class WorkspaceMenuItem extends StatelessWidget { return BlocProvider( create: (_) => WorkspaceMemberBloc(userProfile: userProfile, workspace: workspace) - ..add(const WorkspaceMemberEvent.initial()) - ..add(const WorkspaceMemberEvent.getWorkspaceMembers()), + ..add(const WorkspaceMemberEvent.initial()), child: BlocBuilder( builder: (context, state) { final members = state.members; diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart index 2d9397d7cf..aaa63c3b9b 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart @@ -38,6 +38,8 @@ class WorkspaceMemberBloc workspaceId = ''; }); } + + add(const WorkspaceMemberEvent.getWorkspaceMembers()); }, getWorkspaceMembers: () async { final members = await _getWorkspaceMembers(); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_page.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_page.dart index 724c9ad2be..14ff872cdb 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_page.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/members/workspace_member_page.dart @@ -27,9 +27,7 @@ class WorkspaceMembersPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => WorkspaceMemberBloc(userProfile: userProfile) - ..add( - const WorkspaceMemberEvent.getWorkspaceMembers(), - ), + ..add(const WorkspaceMemberEvent.initial()), child: BlocBuilder( builder: (context, state) { return SingleChildScrollView( From a1823fa4c07cb760fbc7add61e6fbe635fdaf39b Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 7 Mar 2024 09:28:58 +0800 Subject: [PATCH 06/13] feat: feature flag in settings page (#4833) --- .../lib/core/config/kv_keys.dart | 6 ++ .../lib/shared/appflowy_cache_manager.dart | 13 ++++ .../lib/shared/feature_flags.dart | 73 ++++++++++++++++++- .../lib/startup/deps_resolver.dart | 3 +- .../appflowy_flutter/lib/startup/startup.dart | 2 + .../lib/startup/tasks/feature_flag_task.dart | 21 ++++++ .../settings/settings_dialog_bloc.dart | 1 + .../settings/settings_dialog.dart | 3 + .../feature_flags/feature_flag_page.dart | 70 ++++++++++++++++++ .../settings/widgets/settings_menu.dart | 10 +++ .../lib/widget/separated_flex.dart | 7 +- 11 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/startup/tasks/feature_flag_task.dart create mode 100644 frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart diff --git a/frontend/appflowy_flutter/lib/core/config/kv_keys.dart b/frontend/appflowy_flutter/lib/core/config/kv_keys.dart index e4be228133..94103ffef8 100644 --- a/frontend/appflowy_flutter/lib/core/config/kv_keys.dart +++ b/frontend/appflowy_flutter/lib/core/config/kv_keys.dart @@ -58,4 +58,10 @@ class KVKeys { /// The value range is from 0.8 to 1.0. If it's greater than 1.0, it will cause /// the text to be too large and not aligned with the icon static const String textScaleFactor = 'textScaleFactor'; + + /// The key for saving the feature flags + /// + /// The value is a json string with the following format: + /// {'feature_flag_1': true, 'feature_flag_2': false} + static const String featureFlag = 'featureFlag'; } diff --git a/frontend/appflowy_flutter/lib/shared/appflowy_cache_manager.dart b/frontend/appflowy_flutter/lib/shared/appflowy_cache_manager.dart index be62cc44be..4036d37b77 100644 --- a/frontend/appflowy_flutter/lib/shared/appflowy_cache_manager.dart +++ b/frontend/appflowy_flutter/lib/shared/appflowy_cache_manager.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy_backend/log.dart'; import 'package:path_provider/path_provider.dart'; @@ -59,3 +60,15 @@ class TemporaryDirectoryCache implements ICache { await tmpDir.delete(recursive: true); } } + +class FeatureFlagCache implements ICache { + @override + Future cacheSize() async { + return 0; + } + + @override + Future clearAll() async { + await FeatureFlag.clear(); + } +} diff --git a/frontend/appflowy_flutter/lib/shared/feature_flags.dart b/frontend/appflowy_flutter/lib/shared/feature_flags.dart index 8e94c46d61..83a2a341a0 100644 --- a/frontend/appflowy_flutter/lib/shared/feature_flags.dart +++ b/frontend/appflowy_flutter/lib/shared/feature_flags.dart @@ -1,10 +1,17 @@ +import 'dart:collection'; +import 'dart:convert'; + +import 'package:appflowy/core/config/kv.dart'; +import 'package:appflowy/core/config/kv_keys.dart'; +import 'package:appflowy/startup/startup.dart'; + +typedef FeatureFlagMap = Map; + /// The [FeatureFlag] is used to control the front-end features of the app. /// /// For example, if your feature is still under development, /// you can set the value to `false` to hide the feature. enum FeatureFlag { - // Feature flags - // used to control the visibility of the collaborative workspace feature // if it's on, you can see the workspace list and the workspace settings // in the top-left corner of the app @@ -14,7 +21,56 @@ enum FeatureFlag { // if it's on, you can see the members settings in the settings page membersSettings; + static Future initialize() async { + final values = await getIt().getWithFormat( + KVKeys.featureFlag, + (value) => Map.from(jsonDecode(value)).map( + (key, value) => MapEntry( + FeatureFlag.values.firstWhere((e) => e.name == key), + value as bool, + ), + ), + ) ?? + {}; + + _values = { + ...{for (final flag in FeatureFlag.values) flag: false}, + ...values, + }; + } + + static UnmodifiableMapView get data => + UnmodifiableMapView(_values); + + Future turnOn() async { + await update(true); + } + + Future turnOff() async { + await update(false); + } + + Future update(bool value) async { + _values[this] = value; + + await getIt().set( + KVKeys.featureFlag, + jsonEncode( + _values.map((key, value) => MapEntry(key.name, value)), + ), + ); + } + + static Future clear() async { + _values = {}; + await getIt().remove(KVKeys.featureFlag); + } + bool get isOn { + if (_values.containsKey(this)) { + return _values[this]!; + } + switch (this) { case FeatureFlag.collaborativeWorkspace: return false; @@ -22,4 +78,17 @@ enum FeatureFlag { return false; } } + + String get description { + switch (this) { + case FeatureFlag.collaborativeWorkspace: + return 'if it\'s on, you can see the workspace list and the workspace settings in the top-left corner of the app'; + case FeatureFlag.membersSettings: + return 'if it\'s on, you can see the members settings in the settings page'; + } + } + + String get key => 'appflowy_feature_flag_${toString()}'; } + +FeatureFlagMap _values = {}; diff --git a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart index d9e1692dd7..c2759ab2c8 100644 --- a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart +++ b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart @@ -134,7 +134,8 @@ void _resolveCommonService( getIt.registerFactory( () => FlowyCacheManager() ..registerCache(TemporaryDirectoryCache()) - ..registerCache(CustomImageCacheManager()), + ..registerCache(CustomImageCacheManager()) + ..registerCache(FeatureFlagCache()), ); } diff --git a/frontend/appflowy_flutter/lib/startup/startup.dart b/frontend/appflowy_flutter/lib/startup/startup.dart index 4b5c973468..38a4911da8 100644 --- a/frontend/appflowy_flutter/lib/startup/startup.dart +++ b/frontend/appflowy_flutter/lib/startup/startup.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:appflowy/env/cloud_env.dart'; +import 'package:appflowy/startup/tasks/feature_flag_task.dart'; import 'package:appflowy/workspace/application/settings/prelude.dart'; import 'package:appflowy_backend/appflowy_backend.dart'; import 'package:flutter/foundation.dart'; @@ -113,6 +114,7 @@ class FlowyRunner { // there's a flag named _enable in memory_leak_detector.dart. If it's false, the task will be ignored. MemoryLeakDetectorTask(), const DebugTask(), + const FeatureFlagTask(), // localization const InitLocalizationTask(), diff --git a/frontend/appflowy_flutter/lib/startup/tasks/feature_flag_task.dart b/frontend/appflowy_flutter/lib/startup/tasks/feature_flag_task.dart new file mode 100644 index 0000000000..fd2439c892 --- /dev/null +++ b/frontend/appflowy_flutter/lib/startup/tasks/feature_flag_task.dart @@ -0,0 +1,21 @@ +import 'package:appflowy/shared/feature_flags.dart'; +import 'package:flutter/foundation.dart'; + +import '../startup.dart'; + +class FeatureFlagTask extends LaunchTask { + const FeatureFlagTask(); + + @override + Future initialize(LaunchContext context) async { + // the hotkey manager is not supported on mobile + if (!kDebugMode) { + return; + } + + await FeatureFlag.initialize(); + } + + @override + Future dispose() async {} +} diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/settings_dialog_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/settings_dialog_bloc.dart index 8f40d706f6..2d73d6ebe7 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/settings_dialog_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/settings_dialog_bloc.dart @@ -17,6 +17,7 @@ enum SettingsPage { cloud, shortcuts, member, + featureFlags, } class SettingsDialogBloc diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart index c24a6a9fab..484212a011 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart @@ -1,6 +1,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart'; +import 'package:appflowy/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_page.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance_view.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_customize_shortcuts_view.dart'; @@ -114,6 +115,8 @@ class SettingsDialog extends StatelessWidget { return const SettingsCustomizeShortcutsWrapper(); case SettingsPage.member: return WorkspaceMembersPage(userProfile: user); + case SettingsPage.featureFlags: + return const FeatureFlagsPage(); default: return const SizedBox.shrink(); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart new file mode 100644 index 0000000000..da5b16a7fa --- /dev/null +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart @@ -0,0 +1,70 @@ +import 'package:appflowy/shared/feature_flags.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; + +class FeatureFlagsPage extends StatelessWidget { + const FeatureFlagsPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: SeparatedColumn( + children: [ + ...FeatureFlag.data.entries.map( + (e) => _FeatureFlagItem(featureFlag: e.key), + ), + FlowyTextButton( + 'Restart the app to apply changes', + fontSize: 16.0, + fontColor: Colors.red, + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 12.0, + ), + onPressed: () async { + await runAppFlowy(); + }, + ), + ], + ), + ); + } +} + +class _FeatureFlagItem extends StatefulWidget { + const _FeatureFlagItem({ + required this.featureFlag, + }); + + final FeatureFlag featureFlag; + + @override + State<_FeatureFlagItem> createState() => _FeatureFlagItemState(); +} + +class _FeatureFlagItemState extends State<_FeatureFlagItem> { + @override + Widget build(BuildContext context) { + return ListTile( + title: FlowyText( + widget.featureFlag.name, + fontSize: 16.0, + ), + subtitle: FlowyText.small( + widget.featureFlag.description, + maxLines: 3, + ), + trailing: Switch( + value: widget.featureFlag.isOn, + onChanged: (value) { + setState(() { + widget.featureFlag.update(value); + }); + }, + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart index 278194bb4d..f9ae9b3124 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart @@ -4,6 +4,7 @@ import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dar import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class SettingsMenu extends StatelessWidget { @@ -79,6 +80,15 @@ class SettingsMenu extends StatelessWidget { icon: Icons.people, changeSelectedPage: changeSelectedPage, ), + if (kDebugMode) + SettingsMenuElement( + // no need to translate this page + page: SettingsPage.featureFlags, + selectedPage: currentPage, + label: 'Feature Flags', + icon: Icons.flag, + changeSelectedPage: changeSelectedPage, + ), ], ), ); diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/separated_flex.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/separated_flex.dart index 9b2cac25e4..c59c15e73a 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/separated_flex.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/separated_flex.dart @@ -2,6 +2,9 @@ import 'package:flutter/material.dart'; typedef SeparatorBuilder = Widget Function(); +Widget _defaultColumnSeparatorBuilder() => const Divider(); +Widget _defaultRowSeparatorBuilder() => const VerticalDivider(); + class SeparatedColumn extends Column { SeparatedColumn({ super.key, @@ -11,7 +14,7 @@ class SeparatedColumn extends Column { super.textBaseline, super.textDirection, super.verticalDirection, - required SeparatorBuilder separatorBuilder, + SeparatorBuilder separatorBuilder = _defaultColumnSeparatorBuilder, required List children, }) : super(children: _insertSeparators(children, separatorBuilder)); } @@ -25,7 +28,7 @@ class SeparatedRow extends Row { super.textBaseline, super.textDirection, super.verticalDirection, - required SeparatorBuilder separatorBuilder, + SeparatorBuilder separatorBuilder = _defaultRowSeparatorBuilder, required List children, }) : super(children: _insertSeparators(children, separatorBuilder)); } From 90ebcb4cfc2a0e20f58efbfdb0cb9e57914173ed Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:12:34 +0800 Subject: [PATCH 07/13] chore: enable relation (#4835) --- .../grid/presentation/widgets/header/field_type_list.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/field_type_list.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/field_type_list.dart index 3cc9bd1c72..4451a52b6f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/field_type_list.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/field_type_list.dart @@ -20,6 +20,7 @@ const List _supportedFieldTypes = [ FieldType.URL, FieldType.LastEditedTime, FieldType.CreatedTime, + FieldType.Relation, ]; class FieldTypeList extends StatelessWidget with FlowyOverlayDelegate { From 88d14e7bdec1710bec26cd33014e205d4b576beb Mon Sep 17 00:00:00 2001 From: Zack Date: Thu, 7 Mar 2024 10:15:50 +0800 Subject: [PATCH 08/13] fix: delete workspace no panic (#4832) --- .../flowy-user/src/user_manager/manager_user_workspace.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index dfed83ccfe..f94f955ba9 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use collab_entity::{CollabObject, CollabType}; use collab_integrate::CollabKVDB; -use tracing::{error, info, instrument}; +use tracing::{error, info, instrument, warn}; use flowy_error::{FlowyError, FlowyResult}; use flowy_folder_pub::entities::{AppFlowyData, ImportData}; @@ -336,6 +336,8 @@ pub fn delete_user_workspaces(mut conn: DBConnection, workspace_id: &str) -> Flo .execute(conn)?; Ok::(rows_affected) })?; - assert_eq!(n, 1); + if n != 1 { + warn!("expected to delete 1 row, but deleted {} rows", n); + } Ok(()) } From 270d1c68b29a68d5e04edb4b447be8e857d562e2 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 7 Mar 2024 11:37:51 +0800 Subject: [PATCH 09/13] fix: relation hotfix (#4837) --- .../field/type_options/relation_type_option/relation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs index 357729a0c8..4bc7fd3d08 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs @@ -60,7 +60,7 @@ impl CellDataChangeset for RelationTypeOption { let cell_data: RelationCellData = cell.unwrap().as_ref().into(); let mut row_ids = cell_data.row_ids.clone(); for inserted in changeset.inserted_row_ids.iter() { - if row_ids.iter().any(|row_id| row_id == inserted) { + if !row_ids.iter().any(|row_id| row_id == inserted) { row_ids.push(inserted.clone()) } } From 780f08dd288642d30b85a5f353c9f54c1c91f5fc Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 7 Mar 2024 12:48:51 +0800 Subject: [PATCH 10/13] test: execute the integration tests concurrently (#4831) --- .github/actions/flutter_build/action.yml | 104 ++++++ .../flutter_integration_test/action.yml | 78 +++++ .github/workflows/flutter_ci.yaml | 317 ++++++------------ .../integration_test/desktop_runner.dart | 68 ---- .../integration_test/desktop_runner_1.dart | 21 ++ .../integration_test/desktop_runner_2.dart | 40 +++ .../integration_test/desktop_runner_3.dart | 36 ++ .../integration_test/mobile_runner.dart | 3 + .../integration_test/runner.dart | 11 +- 9 files changed, 392 insertions(+), 286 deletions(-) create mode 100644 .github/actions/flutter_build/action.yml create mode 100644 .github/actions/flutter_integration_test/action.yml delete mode 100644 frontend/appflowy_flutter/integration_test/desktop_runner.dart create mode 100644 frontend/appflowy_flutter/integration_test/desktop_runner_1.dart create mode 100644 frontend/appflowy_flutter/integration_test/desktop_runner_2.dart create mode 100644 frontend/appflowy_flutter/integration_test/desktop_runner_3.dart diff --git a/.github/actions/flutter_build/action.yml b/.github/actions/flutter_build/action.yml new file mode 100644 index 0000000000..cb7b325e82 --- /dev/null +++ b/.github/actions/flutter_build/action.yml @@ -0,0 +1,104 @@ +name: Flutter Integration Test +description: Run integration tests for AppFlowy + +inputs: + os: + description: "The operating system to run the tests on" + required: true + flutter_version: + description: "The version of Flutter to use" + required: true + rust_toolchain: + description: "The version of Rust to use" + required: true + cargo_make_version: + description: "The version of cargo-make to use" + required: true + rust_target: + description: "The target to build for" + required: true + flutter_profile: + description: "The profile to build with" + required: true + +runs: + using: "composite" + + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + id: rust_toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ inputs.rust_toolchain }} + target: ${{ inputs.rust_target }} + override: true + profile: minimal + + - name: Export pub environment variables and add to PATH + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + echo "PUB_CACHE=$LOCALAPPDATA\\Pub\\Cache" >> $GITHUB_ENV + fi + shell: bash + + - name: Install flutter + id: flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ inputs.flutter_version }} + cache: true + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ inputs.os }} + workspaces: | + frontend/rust-lib + cache-all-crates: true + + - uses: taiki-e/install-action@v2 + with: + tool: cargo-make@${{ inputs.cargo_make_version }}, duckscript_cli + + - name: Install prerequisites + working-directory: frontend + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub + sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list + sudo apt-get update + sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev + elif [ "$RUNNER_OS" == "Windows" ]; then + vcpkg integrate install + elif [ "$RUNNER_OS" == "macOS" ]; then + echo 'do nothing' + fi + cargo make appflowy-flutter-deps-tools + shell: bash + + - name: Build AppFlowy + working-directory: frontend + run: cargo make --profile ${{ inputs.flutter_profile }} appflowy-core-dev + shell: bash + + - name: Run code generation + working-directory: frontend + run: cargo make code_generation + shell: bash + + - name: Flutter Analyzer + working-directory: frontend/appflowy_flutter + run: flutter analyze . + shell: bash + + - name: Compress appflowy_flutter + run: tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter + shell: bash + + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.run_id }}-${{ matrix.os }} + path: appflowy_flutter.tar.gz \ No newline at end of file diff --git a/.github/actions/flutter_integration_test/action.yml b/.github/actions/flutter_integration_test/action.yml new file mode 100644 index 0000000000..6df3ec005d --- /dev/null +++ b/.github/actions/flutter_integration_test/action.yml @@ -0,0 +1,78 @@ +name: Flutter Integration Test +description: Run integration tests for AppFlowy + +inputs: + test_path: + description: "The path to the integration test file" + required: true + flutter_version: + description: "The version of Flutter to use" + required: true + rust_toolchain: + description: "The version of Rust to use" + required: true + cargo_make_version: + description: "The version of cargo-make to use" + required: true + rust_target: + description: "The target to build for" + required: true + +runs: + using: "composite" + + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + id: rust_toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ inputs.RUST_TOOLCHAIN }} + target: ${{ inputs.rust_target }} + override: true + profile: minimal + + - name: Install flutter + id: flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ inputs.flutter_version }} + cache: true + + - uses: taiki-e/install-action@v2 + with: + tool: cargo-make@${{ inputs.cargo_make_version }} + + - name: Install prerequisites + working-directory: frontend + run: | + sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub + sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list + sudo apt-get update + sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager + shell: bash + + - name: Enable Flutter Desktop + run: | + flutter config --enable-linux-desktop + shell: bash + + - uses: actions/download-artifact@v4 + with: + name: ${{ github.run_id }}-ubuntu-latest + + - name: Uncompressed appflowy_flutter + run: tar -xf appflowy_flutter.tar.gz + shell: bash + + - name: Run Flutter integration tests + working-directory: frontend/appflowy_flutter + run: | + export DISPLAY=:99 + sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & + sudo apt-get install network-manager + flutter test ${{ inputs.test_path }} -d Linux --coverage + shell: bash \ No newline at end of file diff --git a/.github/workflows/flutter_ci.yaml b/.github/workflows/flutter_ci.yaml index d3ddc17be8..a5cb5f8f96 100644 --- a/.github/workflows/flutter_ci.yaml +++ b/.github/workflows/flutter_ci.yaml @@ -32,28 +32,21 @@ concurrency: cancel-in-progress: true jobs: - prepare: + prepare-linux: if: github.event.pull_request.draft != true strategy: - fail-fast: false + fail-fast: true matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest] include: - os: ubuntu-latest flutter_profile: development-linux-x86_64 target: x86_64-unknown-linux-gnu - - os: macos-latest - flutter_profile: development-mac-x86_64 - target: x86_64-apple-darwin - - os: windows-latest - flutter_profile: development-windows-x86 - target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: # the following step is required to avoid running out of space - name: Maximize build space - if: matrix.os == 'ubuntu-latest' run: | sudo rm -rf /usr/share/dotnet sudo rm -rf /opt/ghc @@ -63,80 +56,70 @@ jobs: - name: Checkout source code uses: actions/checkout@v4 - - name: Install Rust toolchain - id: rust_toolchain - uses: actions-rs/toolchain@v1 + - name: Flutter build + uses: ./.github/actions/flutter_build with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - target: ${{ matrix.target }} - override: true - profile: minimal + os: ${{ matrix.os }} + flutter_version: ${{ env.FLUTTER_VERSION }} + rust_toolchain: ${{ env.RUST_TOOLCHAIN }} + cargo_make_version: ${{ env.CARGO_MAKE_VERSION }} + rust_target: ${{ matrix.target }} + flutter_profile: ${{ matrix.flutter_profile }} - - name: Export pub environment variables and add to PATH - run: | - if [ "$RUNNER_OS" == "Windows" ]; then - echo "PUB_CACHE=$LOCALAPPDATA\\Pub\\Cache" >> $GITHUB_ENV - fi - shell: bash + prepare-windows: + if: github.event.pull_request.draft != true + strategy: + fail-fast: true + matrix: + os: [windows-latest] + include: + - os: windows-latest + flutter_profile: development-windows-x86 + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} - - name: Install flutter - id: flutter - uses: subosito/flutter-action@v2 + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Flutter build + uses: ./.github/actions/flutter_build with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true + os: ${{ matrix.os }} + flutter_version: ${{ env.FLUTTER_VERSION }} + rust_toolchain: ${{ env.RUST_TOOLCHAIN }} + cargo_make_version: ${{ env.CARGO_MAKE_VERSION }} + rust_target: ${{ matrix.target }} + flutter_profile: ${{ matrix.flutter_profile }} - - uses: Swatinem/rust-cache@v2 + prepare-macos: + if: github.event.pull_request.draft != true + strategy: + fail-fast: true + matrix: + os: [macos-latest] + include: + - os: macos-latest + flutter_profile: development-mac-x86_64 + target: x86_64-apple-darwin + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Flutter build + uses: ./.github/actions/flutter_build with: - prefix-key: ${{ matrix.os }} - workspaces: | - frontend/rust-lib - cache-all-crates: true - - - uses: taiki-e/install-action@v2 - with: - tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}, duckscript_cli - - - name: Install prerequisites - working-directory: frontend - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub - sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list - sudo apt-get update - sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev - elif [ "$RUNNER_OS" == "Windows" ]; then - vcpkg integrate install - elif [ "$RUNNER_OS" == "macOS" ]; then - echo 'do nothing' - fi - cargo make appflowy-flutter-deps-tools - shell: bash - - - name: Build AppFlowy - working-directory: frontend - run: cargo make --profile ${{ matrix.flutter_profile }} appflowy-core-dev - - - name: Run code generation - working-directory: frontend - run: cargo make code_generation - - - name: Flutter Analyzer - working-directory: frontend/appflowy_flutter - run: flutter analyze . - - - name: Compress appflowy_flutter - run: | - tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter - - - uses: actions/upload-artifact@v4 - with: - name: ${{ github.run_id }}-${{ matrix.os }} - path: appflowy_flutter.tar.gz + os: ${{ matrix.os }} + flutter_version: ${{ env.FLUTTER_VERSION }} + rust_toolchain: ${{ env.RUST_TOOLCHAIN }} + cargo_make_version: ${{ env.CARGO_MAKE_VERSION }} + rust_target: ${{ matrix.target }} + flutter_profile: ${{ matrix.flutter_profile }} unit_test: - needs: [prepare] + needs: [prepare-linux] if: github.event.pull_request.draft != true strategy: fail-fast: false @@ -227,7 +210,7 @@ jobs: shell: bash cloud_integration_test: - needs: [prepare] + needs: [prepare-linux] strategy: fail-fast: false matrix: @@ -314,8 +297,9 @@ jobs: flutter test integration_test/cloud/cloud_runner.dart -d Linux --coverage shell: bash - integration_test: - needs: [prepare] + # split the integration tests into different machines to minimize the time + integration_test_1: + needs: [prepare-linux] if: github.event.pull_request.draft != true strategy: fail-fast: false @@ -323,158 +307,65 @@ jobs: os: [ubuntu-latest] include: - os: ubuntu-latest - flutter_profile: development-linux-x86_64 - target: x86_64-unknown-linux-gnu + target: 'x86_64-unknown-linux-gnu' runs-on: ${{ matrix.os }} - steps: - name: Checkout source code uses: actions/checkout@v4 - - name: Install Rust toolchain - id: rust_toolchain - uses: actions-rs/toolchain@v1 + - name: Flutter Integration Test 1 + uses: ./.github/actions/flutter_integration_test with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - target: ${{ matrix.target }} - override: true - profile: minimal + test_path: integration_test/desktop_runner_1.dart + flutter_version: ${{ env.FLUTTER_VERSION }} + rust_toolchain: ${{ env.RUST_TOOLCHAIN }} + cargo_make_version: ${{ env.CARGO_MAKE_VERSION }} + rust_target: ${{ matrix.target }} - - name: Install flutter - id: flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - uses: taiki-e/install-action@v2 - with: - tool: cargo-make@${{ env.CARGO_MAKE_VERSION }} - - - name: Install prerequisites - working-directory: frontend - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub - sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list - sudo apt-get update - sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev - fi - shell: bash - - - name: Enable Flutter Desktop - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - flutter config --enable-linux-desktop - elif [ "$RUNNER_OS" == "macOS" ]; then - flutter config --enable-macos-desktop - elif [ "$RUNNER_OS" == "Windows" ]; then - git config --system core.longpaths true - flutter config --enable-windows-desktop - fi - shell: bash - - - uses: actions/download-artifact@v4 - with: - name: ${{ github.run_id }}-${{ matrix.os }} - - - name: Uncompressed appflowy_flutter - run: tar -xf appflowy_flutter.tar.gz - - - name: Run flutter pub get - working-directory: frontend - run: cargo make pub_get - - - name: Run Flutter integration tests - working-directory: frontend/appflowy_flutter - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - export DISPLAY=:99 - sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & - sudo apt-get install network-manager - flutter test integration_test/runner.dart -d Linux --coverage - elif [ "$RUNNER_OS" == "macOS" ]; then - flutter test integration_test/runner.dart -d macOS --coverage - elif [ "$RUNNER_OS" == "Windows" ]; then - flutter test integration_test/runner.dart -d Windows --coverage - fi - shell: bash - build: - needs: [prepare] + integration_test_2: + needs: [prepare-linux] if: github.event.pull_request.draft != true strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest] include: - os: ubuntu-latest - flutter_profile: development-linux-x86_64 - target: x86_64-unknown-linux-gnu - - os: macos-latest - flutter_profile: development-mac-x86_64 - target: x86_64-apple-darwin - - os: windows-latest - flutter_profile: development-windows-x86 - target: x86_64-pc-windows-msvc + target: 'x86_64-unknown-linux-gnu' runs-on: ${{ matrix.os }} - steps: - name: Checkout source code uses: actions/checkout@v4 - - name: Install Rust toolchain - id: rust_toolchain - uses: actions-rs/toolchain@v1 + - name: Flutter Integration Test 2 + uses: ./.github/actions/flutter_integration_test with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - target: ${{ matrix.target }} - override: true - profile: minimal + test_path: integration_test/desktop_runner_2.dart + flutter_version: ${{ env.FLUTTER_VERSION }} + rust_toolchain: ${{ env.RUST_TOOLCHAIN }} + cargo_make_version: ${{ env.CARGO_MAKE_VERSION }} + rust_target: ${{ matrix.target }} - - name: Install flutter - id: flutter - uses: subosito/flutter-action@v2 + integration_test_3: + needs: [prepare-linux] + if: github.event.pull_request.draft != true + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + include: + - os: ubuntu-latest + target: 'x86_64-unknown-linux-gnu' + runs-on: ${{ matrix.os }} + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Flutter Integration Test 3 + uses: ./.github/actions/flutter_integration_test with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - uses: taiki-e/install-action@v2 - with: - tool: cargo-make@${{ env.CARGO_MAKE_VERSION }} - - - name: Install prerequisites - working-directory: frontend - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub - sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list - sudo apt-get update - sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev - fi - shell: bash - - - name: Enable Flutter Desktop - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - flutter config --enable-linux-desktop - elif [ "$RUNNER_OS" == "macOS" ]; then - flutter config --enable-macos-desktop - elif [ "$RUNNER_OS" == "Windows" ]; then - git config --system core.longpaths true - flutter config --enable-windows-desktop - fi - shell: bash - - - uses: actions/download-artifact@v4 - with: - name: ${{ github.run_id }}-${{ matrix.os }} - - - name: Uncompressed appflowy_flutter - run: tar -xf appflowy_flutter.tar.gz - - - name: Build flutter product - working-directory: frontend - run: | - cargo make --profile ${{ matrix.flutter_profile }} appflowy-make-product-dev + test_path: integration_test/desktop_runner_3.dart + flutter_version: ${{ env.FLUTTER_VERSION }} + rust_toolchain: ${{ env.RUST_TOOLCHAIN }} + cargo_make_version: ${{ env.CARGO_MAKE_VERSION }} + rust_target: ${{ matrix.target }} \ No newline at end of file diff --git a/frontend/appflowy_flutter/integration_test/desktop_runner.dart b/frontend/appflowy_flutter/integration_test/desktop_runner.dart deleted file mode 100644 index 66532d947c..0000000000 --- a/frontend/appflowy_flutter/integration_test/desktop_runner.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'desktop/uncategorized/appearance_settings_test.dart' as appearance_test_runner; -import 'desktop/board/board_test_runner.dart' as board_test_runner; -import 'desktop/database/database_calendar_test.dart' as database_calendar_test; -import 'desktop/database/database_cell_test.dart' as database_cell_test; -import 'desktop/database/database_field_settings_test.dart' - as database_field_settings_test; -import 'desktop/database/database_field_test.dart' as database_field_test; -import 'desktop/database/database_filter_test.dart' as database_filter_test; -import 'desktop/database/database_row_page_test.dart' as database_row_page_test; -import 'desktop/database/database_row_test.dart' as database_row_test; -import 'desktop/database/database_setting_test.dart' as database_setting_test; -import 'desktop/database/database_share_test.dart' as database_share_test; -import 'desktop/database/database_sort_test.dart' as database_sort_test; -import 'desktop/database/database_view_test.dart' as database_view_test; -import 'desktop/document/document_test_runner.dart' as document_test_runner; -import 'desktop/uncategorized/emoji_shortcut_test.dart' as emoji_shortcut_test; -import 'desktop/uncategorized/empty_test.dart' as first_test; -import 'desktop/uncategorized/hotkeys_test.dart' as hotkeys_test; -import 'desktop/uncategorized/import_files_test.dart' as import_files_test; -import 'desktop/settings/settings_runner.dart' as settings_test_runner; -import 'desktop/uncategorized/share_markdown_test.dart' as share_markdown_test; -import 'desktop/sidebar/sidebar_test_runner.dart' as sidebar_test_runner; -import 'desktop/uncategorized/switch_folder_test.dart' as switch_folder_test; -import 'desktop/uncategorized/tabs_test.dart' as tabs_test; - -Future runIntegrationOnDesktop() async { - // This test must be run first, otherwise the CI will fail. - first_test.main(); - - switch_folder_test.main(); - share_markdown_test.main(); - import_files_test.main(); - - // Document integration tests - document_test_runner.startTesting(); - - // Sidebar integration tests - sidebar_test_runner.startTesting(); - - // Board integration test - board_test_runner.startTesting(); - - // Database integration tests - database_cell_test.main(); - database_field_test.main(); - database_field_settings_test.main(); - database_share_test.main(); - database_row_page_test.main(); - database_row_test.main(); - database_setting_test.main(); - database_filter_test.main(); - database_sort_test.main(); - database_view_test.main(); - database_calendar_test.main(); - - // Tabs - tabs_test.main(); - - // Others - hotkeys_test.main(); - emoji_shortcut_test.main(); - - // Appearance integration test - appearance_test_runner.main(); - - // User settings - settings_test_runner.main(); -} diff --git a/frontend/appflowy_flutter/integration_test/desktop_runner_1.dart b/frontend/appflowy_flutter/integration_test/desktop_runner_1.dart new file mode 100644 index 0000000000..e972f49fbb --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop_runner_1.dart @@ -0,0 +1,21 @@ +import 'package:integration_test/integration_test.dart'; + +import 'desktop/document/document_test_runner.dart' as document_test_runner; +import 'desktop/uncategorized/empty_test.dart' as first_test; +import 'desktop/uncategorized/switch_folder_test.dart' as switch_folder_test; + +Future main() async { + await runIntegration1OnDesktop(); +} + +Future runIntegration1OnDesktop() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // This test must be run first, otherwise the CI will fail. + first_test.main(); + + switch_folder_test.main(); + document_test_runner.startTesting(); + + // DON'T add more tests here. This is the first test runner for desktop. +} diff --git a/frontend/appflowy_flutter/integration_test/desktop_runner_2.dart b/frontend/appflowy_flutter/integration_test/desktop_runner_2.dart new file mode 100644 index 0000000000..9053da8d18 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop_runner_2.dart @@ -0,0 +1,40 @@ +import 'package:integration_test/integration_test.dart'; + +import 'desktop/database/database_calendar_test.dart' as database_calendar_test; +import 'desktop/database/database_cell_test.dart' as database_cell_test; +import 'desktop/database/database_field_settings_test.dart' + as database_field_settings_test; +import 'desktop/database/database_field_test.dart' as database_field_test; +import 'desktop/database/database_filter_test.dart' as database_filter_test; +import 'desktop/database/database_row_page_test.dart' as database_row_page_test; +import 'desktop/database/database_row_test.dart' as database_row_test; +import 'desktop/database/database_setting_test.dart' as database_setting_test; +import 'desktop/database/database_share_test.dart' as database_share_test; +import 'desktop/database/database_sort_test.dart' as database_sort_test; +import 'desktop/database/database_view_test.dart' as database_view_test; +import 'desktop/uncategorized/empty_test.dart' as first_test; + +Future main() async { + await runIntegration2OnDesktop(); +} + +Future runIntegration2OnDesktop() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // This test must be run first, otherwise the CI will fail. + first_test.main(); + + database_cell_test.main(); + database_field_test.main(); + database_field_settings_test.main(); + database_share_test.main(); + database_row_page_test.main(); + database_row_test.main(); + database_setting_test.main(); + database_filter_test.main(); + database_sort_test.main(); + database_view_test.main(); + database_calendar_test.main(); + + // DON'T add more tests here. This is the second test runner for desktop. +} diff --git a/frontend/appflowy_flutter/integration_test/desktop_runner_3.dart b/frontend/appflowy_flutter/integration_test/desktop_runner_3.dart new file mode 100644 index 0000000000..09a784d4fc --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop_runner_3.dart @@ -0,0 +1,36 @@ +import 'package:integration_test/integration_test.dart'; + +import 'desktop/board/board_test_runner.dart' as board_test_runner; +import 'desktop/settings/settings_runner.dart' as settings_test_runner; +import 'desktop/sidebar/sidebar_test_runner.dart' as sidebar_test_runner; +import 'desktop/uncategorized/appearance_settings_test.dart' + as appearance_test_runner; +import 'desktop/uncategorized/emoji_shortcut_test.dart' as emoji_shortcut_test; +import 'desktop/uncategorized/empty_test.dart' as first_test; +import 'desktop/uncategorized/hotkeys_test.dart' as hotkeys_test; +import 'desktop/uncategorized/import_files_test.dart' as import_files_test; +import 'desktop/uncategorized/share_markdown_test.dart' as share_markdown_test; +import 'desktop/uncategorized/tabs_test.dart' as tabs_test; + +Future main() async { + await runIntegration3OnDesktop(); +} + +Future runIntegration3OnDesktop() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // This test must be run first, otherwise the CI will fail. + first_test.main(); + + hotkeys_test.main(); + emoji_shortcut_test.main(); + hotkeys_test.main(); + emoji_shortcut_test.main(); + appearance_test_runner.main(); + settings_test_runner.main(); + share_markdown_test.main(); + import_files_test.main(); + sidebar_test_runner.startTesting(); + board_test_runner.startTesting(); + tabs_test.main(); +} diff --git a/frontend/appflowy_flutter/integration_test/mobile_runner.dart b/frontend/appflowy_flutter/integration_test/mobile_runner.dart index daab343060..ca1a7ae0d3 100644 --- a/frontend/appflowy_flutter/integration_test/mobile_runner.dart +++ b/frontend/appflowy_flutter/integration_test/mobile_runner.dart @@ -1,5 +1,8 @@ +import 'package:integration_test/integration_test.dart'; + import 'mobile/sign_in/anonymous_sign_in_test.dart' as anonymous_sign_in_test; Future runIntegrationOnMobile() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); anonymous_sign_in_test.main(); } diff --git a/frontend/appflowy_flutter/integration_test/runner.dart b/frontend/appflowy_flutter/integration_test/runner.dart index 39f0bf45e1..cb7d2d6e33 100644 --- a/frontend/appflowy_flutter/integration_test/runner.dart +++ b/frontend/appflowy_flutter/integration_test/runner.dart @@ -1,8 +1,8 @@ import 'dart:io'; -import 'package:integration_test/integration_test.dart'; - -import 'desktop_runner.dart'; +import 'desktop_runner_1.dart'; +import 'desktop_runner_2.dart'; +import 'desktop_runner_3.dart'; import 'mobile_runner.dart'; /// The main task runner for all integration tests in AppFlowy. @@ -13,9 +13,10 @@ import 'mobile_runner.dart'; /// Once removed, the integration_test.yaml must be updated to exclude this as /// as the test target. Future main() async { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { - await runIntegrationOnDesktop(); + await runIntegration1OnDesktop(); + await runIntegration2OnDesktop(); + await runIntegration3OnDesktop(); } else if (Platform.isIOS || Platform.isAndroid) { await runIntegrationOnMobile(); } else { From 3d8ff062b5f5992005752f9045f7dd7b8fca603c Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:50:28 +0800 Subject: [PATCH 11/13] chore: bump client api (#4819) * chore: bump client api * chore: bump client api * chore: update ci * chore: update client api * chore: ci config * chore: bump client api * chore: bump client api * chore: bump client api --- .github/workflows/rust_ci.yaml | 21 ++++++ .../appflowy_flutter/lib/env/cloud_env.dart | 4 +- frontend/appflowy_tauri/src-tauri/Cargo.lock | 32 ++++---- frontend/appflowy_tauri/src-tauri/Cargo.toml | 2 +- frontend/appflowy_web/wasm-libs/Cargo.lock | 32 +++++--- frontend/appflowy_web/wasm-libs/Cargo.toml | 2 +- .../appflowy_web/wasm-libs/af-wasm/src/lib.rs | 4 +- .../wasm-libs/af-wasm/tests/util/tester.rs | 2 +- frontend/rust-lib/Cargo.lock | 34 +++++---- frontend/rust-lib/Cargo.toml | 2 +- .../event-integration/src/user_event.rs | 4 +- frontend/rust-lib/flowy-database2/build.rs | 13 ++-- .../af_cloud/impls/user/cloud_service_impl.rs | 2 +- .../flowy-server/src/af_cloud/server.rs | 29 +++----- frontend/rust-lib/flowy-server/src/server.rs | 2 +- .../flowy-server/tests/af_cloud_test/util.rs | 4 +- .../rust-lib/flowy-user/src/services/db.rs | 74 ++++++++++--------- 17 files changed, 151 insertions(+), 112 deletions(-) diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index b4062980b5..9cca48942d 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -25,6 +25,22 @@ jobs: test-on-ubuntu: runs-on: ubuntu-latest steps: +# - name: Maximize build space +# uses: easimon/maximize-build-space@master +# with: +# root-reserve-mb: 2048 +# swap-size-mb: 1024 +# remove-dotnet: 'true' +# +# # the following step is required to avoid running out of space +# - name: Maximize build space +# run: | +# sudo rm -rf /usr/share/dotnet +# sudo rm -rf /opt/ghc +# sudo rm -rf "/usr/local/share/boost" +# sudo rm -rf "$AGENT_TOOLSDIRECTORY" +# sudo docker image prune --all --force + - name: Checkout source code uses: actions/checkout@v4 @@ -86,3 +102,8 @@ jobs: - name: clippy rust-lib run: cargo clippy --all-targets -- -D warnings working-directory: frontend/rust-lib + + - name: Clean up Docker images + run: | + docker image prune -af + docker volume prune -f diff --git a/frontend/appflowy_flutter/lib/env/cloud_env.dart b/frontend/appflowy_flutter/lib/env/cloud_env.dart index 45dd135e98..27c4469f45 100644 --- a/frontend/appflowy_flutter/lib/env/cloud_env.dart +++ b/frontend/appflowy_flutter/lib/env/cloud_env.dart @@ -281,7 +281,7 @@ Future configurationFromUri( if (authenticatorType == AuthenticatorType.appflowyCloudDevelop) { return AppFlowyCloudConfiguration( base_url: "$baseUrl:8000", - ws_base_url: "ws://${baseUri.host}:8000/ws", + ws_base_url: "ws://${baseUri.host}:8000/ws/v1", gotrue_url: "$baseUrl:9999", ); } else { @@ -319,7 +319,7 @@ Future _getAppFlowyCloudWSUrl(String baseURL) async { // Construct the WebSocket URL directly from the parsed URI. final wsScheme = uri.isScheme('HTTPS') ? 'wss' : 'ws'; - final wsUrl = Uri(scheme: wsScheme, host: uri.host, path: '/ws'); + final wsUrl = Uri(scheme: wsScheme, host: uri.host, path: '/ws/v1'); return wsUrl.toString(); } catch (e) { diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 0e2f08cfbe..20a2e20290 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -162,7 +162,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -714,7 +714,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "again", "anyhow", @@ -741,6 +741,7 @@ dependencies = [ "realtime-protocol", "reqwest", "scraper 0.17.1", + "semver", "serde", "serde_json", "serde_repr", @@ -1312,7 +1313,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -2586,7 +2587,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "futures-util", @@ -2603,7 +2604,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -3058,7 +3059,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "reqwest", @@ -4800,7 +4801,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -4824,7 +4825,7 @@ dependencies = [ [[package]] name = "realtime-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -5317,9 +5318,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -5496,7 +5497,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -6240,9 +6241,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -6992,13 +6993,14 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "futures-channel", "futures-util", "http", "httparse", "js-sys", + "percent-encoding", "thiserror", "tokio", "tokio-tungstenite", @@ -7454,7 +7456,7 @@ dependencies = [ [[package]] name = "workspace-template" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "async-trait", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 9307f3528a..cf031f86d8 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -82,7 +82,7 @@ custom-protocol = ["tauri/custom-protocol"] # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "15c03e4f85fffd35089a82c2a84aca8042a38946" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "eb79b9f5e80846bd06b30b4f9c04039ce1452582" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/appflowy_web/wasm-libs/Cargo.lock b/frontend/appflowy_web/wasm-libs/Cargo.lock index 2274c4fb42..2dac333820 100644 --- a/frontend/appflowy_web/wasm-libs/Cargo.lock +++ b/frontend/appflowy_web/wasm-libs/Cargo.lock @@ -221,7 +221,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -545,7 +545,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "again", "anyhow", @@ -572,6 +572,7 @@ dependencies = [ "realtime-protocol", "reqwest", "scraper 0.17.1", + "semver", "serde", "serde_json", "serde_repr", @@ -945,7 +946,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -1699,7 +1700,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "futures-util", @@ -1716,7 +1717,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -2050,7 +2051,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "reqwest", @@ -3308,7 +3309,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -3332,7 +3333,7 @@ dependencies = [ [[package]] name = "realtime-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -3665,6 +3666,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" version = "1.0.195" @@ -3779,7 +3786,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -4145,9 +4152,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -4721,13 +4728,14 @@ checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "futures-channel", "futures-util", "http", "httparse", "js-sys", + "percent-encoding", "thiserror", "tokio", "tokio-tungstenite", diff --git a/frontend/appflowy_web/wasm-libs/Cargo.toml b/frontend/appflowy_web/wasm-libs/Cargo.toml index 5e56d2551a..e423a30207 100644 --- a/frontend/appflowy_web/wasm-libs/Cargo.toml +++ b/frontend/appflowy_web/wasm-libs/Cargo.toml @@ -55,7 +55,7 @@ codegen-units = 1 # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "15c03e4f85fffd35089a82c2a84aca8042a38946" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "eb79b9f5e80846bd06b30b4f9c04039ce1452582" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/appflowy_web/wasm-libs/af-wasm/src/lib.rs b/frontend/appflowy_web/wasm-libs/af-wasm/src/lib.rs index ab017366bd..efe3855f28 100644 --- a/frontend/appflowy_web/wasm-libs/af-wasm/src/lib.rs +++ b/frontend/appflowy_web/wasm-libs/af-wasm/src/lib.rs @@ -49,14 +49,14 @@ pub fn init_wasm_core() -> js_sys::Promise { #[cfg(feature = "localhost_dev")] let config = AFCloudConfiguration { base_url: "http://localhost".to_string(), - ws_base_url: "ws://localhost/ws".to_string(), + ws_base_url: "ws://localhost/ws/v1".to_string(), gotrue_url: "http://localhost/gotrue".to_string(), }; #[cfg(not(feature = "localhost_dev"))] let config = AFCloudConfiguration { base_url: "https://beta.appflowy.cloud".to_string(), - ws_base_url: "wss://beta.appflowy.cloud/ws".to_string(), + ws_base_url: "wss://beta.appflowy.cloud/ws/v1".to_string(), gotrue_url: "https://beta.appflowy.cloud/gotrue".to_string(), }; diff --git a/frontend/appflowy_web/wasm-libs/af-wasm/tests/util/tester.rs b/frontend/appflowy_web/wasm-libs/af-wasm/tests/util/tester.rs index b8744bdeba..5142d8012f 100644 --- a/frontend/appflowy_web/wasm-libs/af-wasm/tests/util/tester.rs +++ b/frontend/appflowy_web/wasm-libs/af-wasm/tests/util/tester.rs @@ -24,7 +24,7 @@ impl WASMEventTester { setup_log(); let config = AFCloudConfiguration { base_url: "http://localhost".to_string(), - ws_base_url: "ws://localhost/ws".to_string(), + ws_base_url: "ws://localhost/ws/v1".to_string(), gotrue_url: "http://localhost/gotrue".to_string(), }; let core = Arc::new(AppFlowyWASMCore::new("device_id", config).await.unwrap()); diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 595a605df0..9f686f4824 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -163,7 +163,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "again", "anyhow", @@ -700,6 +700,7 @@ dependencies = [ "realtime-protocol", "reqwest", "scraper 0.17.1", + "semver", "serde", "serde_json", "serde_repr", @@ -1236,7 +1237,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -2410,7 +2411,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "futures-util", @@ -2427,7 +2428,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -2821,7 +2822,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "reqwest", @@ -4318,7 +4319,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -4342,7 +4343,7 @@ dependencies = [ [[package]] name = "realtime-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "bincode", @@ -4805,6 +4806,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" version = "1.0.195" @@ -4930,7 +4937,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "app-error", @@ -5364,9 +5371,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -6120,13 +6127,14 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "futures-channel", "futures-util", "http", "httparse", "js-sys", + "percent-encoding", "thiserror", "tokio", "tokio-tungstenite", @@ -6360,7 +6368,7 @@ dependencies = [ [[package]] name = "workspace-template" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=eb79b9f5e80846bd06b30b4f9c04039ce1452582#eb79b9f5e80846bd06b30b4f9c04039ce1452582" dependencies = [ "anyhow", "async-trait", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index bd3aded33c..8be0d557df 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -105,7 +105,7 @@ incremental = false # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "15c03e4f85fffd35089a82c2a84aca8042a38946" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "eb79b9f5e80846bd06b30b4f9c04039ce1452582" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/rust-lib/event-integration/src/user_event.rs b/frontend/rust-lib/event-integration/src/user_event.rs index b348b89d64..4f8b858d6e 100644 --- a/frontend/rust-lib/event-integration/src/user_event.rs +++ b/frontend/rust-lib/event-integration/src/user_event.rs @@ -410,7 +410,7 @@ pub async fn user_localhost_af_cloud() { let base_url = std::env::var("af_cloud_test_base_url").unwrap_or("http://localhost:8000".to_string()); let ws_base_url = - std::env::var("af_cloud_test_ws_url").unwrap_or("ws://localhost:8000/ws".to_string()); + std::env::var("af_cloud_test_ws_url").unwrap_or("ws://localhost:8000/ws/v1".to_string()); let gotrue_url = std::env::var("af_cloud_test_gotrue_url").unwrap_or("http://localhost:9999".to_string()); AFCloudConfiguration { @@ -426,7 +426,7 @@ pub async fn user_localhost_af_cloud() { #[allow(dead_code)] pub async fn user_localhost_af_cloud_with_nginx() { std::env::set_var("af_cloud_test_base_url", "http://localhost"); - std::env::set_var("af_cloud_test_ws_url", "ws://localhost/ws"); + std::env::set_var("af_cloud_test_ws_url", "ws://localhost/ws/v1"); std::env::set_var("af_cloud_test_gotrue_url", "http://localhost/gotrue"); user_localhost_af_cloud().await } diff --git a/frontend/rust-lib/flowy-database2/build.rs b/frontend/rust-lib/flowy-database2/build.rs index 7d0351bcf6..90f29201b0 100644 --- a/frontend/rust-lib/flowy-database2/build.rs +++ b/frontend/rust-lib/flowy-database2/build.rs @@ -1,14 +1,17 @@ fn main() { - let crate_name = env!("CARGO_PKG_NAME"); #[cfg(feature = "dart")] { - flowy_codegen::protobuf_file::dart_gen(crate_name); - flowy_codegen::dart_event::gen(crate_name); + flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME")); + flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME")); } #[cfg(feature = "ts")] { - flowy_codegen::ts_event::gen(crate_name, flowy_codegen::Project::Tauri); - flowy_codegen::protobuf_file::ts_gen(crate_name, crate_name, flowy_codegen::Project::Tauri); + flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri); + flowy_codegen::protobuf_file::ts_gen( + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_NAME"), + flowy_codegen::Project::Tauri, + ); } } diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs index 625790d578..bc3b6f4863 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs @@ -360,7 +360,7 @@ async fn get_admin_client(client: &Arc) -> FlowyResult { client.gotrue_url(), &client.device_id, ClientConfiguration::default(), - &client.client_id, + &client.client_version.to_string(), ); admin_client .sign_in_password(&admin_email, &admin_password) diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/server.rs b/frontend/rust-lib/flowy-server/src/af_cloud/server.rs index f165a86a51..ef5522f9aa 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/server.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/server.rs @@ -49,7 +49,7 @@ impl AppFlowyCloudServer { config: AFCloudConfiguration, enable_sync: bool, mut device_id: String, - app_version: &str, + client_version: &str, ) -> Self { // The device id can't be empty, so we generate a new one if it is. if device_id.is_empty() { @@ -65,7 +65,7 @@ impl AppFlowyCloudServer { ClientConfiguration::default() .with_compression_buffer_size(10240) .with_compression_quality(8), - app_version, + client_version, ); let token_state_rx = api_client.subscribe_token_state(); let enable_sync = Arc::new(AtomicBool::new(enable_sync)); @@ -75,13 +75,7 @@ impl AppFlowyCloudServer { let ws_client = Arc::new(ws_client); let api_client = Arc::new(api_client); - spawn_ws_conn( - &device_id, - token_state_rx, - &ws_client, - &api_client, - &enable_sync, - ); + spawn_ws_conn(token_state_rx, &ws_client, &api_client, &enable_sync); Self { config, client: api_client, @@ -240,13 +234,11 @@ impl AppFlowyServer for AppFlowyCloudServer { /// This function listens to the `token_state_rx` channel for token state updates. Depending on the /// received state, it either refreshes the WebSocket connection or disconnects from it. fn spawn_ws_conn( - device_id: &String, mut token_state_rx: TokenStateReceiver, ws_client: &Arc, api_client: &Arc, enable_sync: &Arc, ) { - let cloned_device_id = device_id.to_owned(); let weak_ws_client = Arc::downgrade(ws_client); let weak_api_client = Arc::downgrade(api_client); let enable_sync = enable_sync.clone(); @@ -257,17 +249,17 @@ fn spawn_ws_conn( while let Ok(state) = state_recv.recv().await { info!("[websocket] state: {:?}", state); match state { - ConnectState::PingTimeout | ConnectState::Closed => { + ConnectState::PingTimeout | ConnectState::Lost => { // Try to reconnect if the connection is timed out. if let Some(api_client) = weak_api_client.upgrade() { if enable_sync.load(Ordering::SeqCst) { - match api_client.ws_url(&cloned_device_id).await { - Ok(ws_addr) => { + match api_client.ws_connect_info().await { + Ok(conn_info) => { // sleep two seconds and then try to reconnect tokio::time::sleep(Duration::from_secs(2)).await; event!(tracing::Level::INFO, "🟢reconnecting websocket"); - let _ = ws_client.connect(ws_addr, &cloned_device_id).await; + let _ = ws_client.connect(&api_client.ws_addr(), conn_info).await; }, Err(err) => error!("Failed to get ws url: {}, connect state:{:?}", err, state), } @@ -287,7 +279,6 @@ fn spawn_ws_conn( } }); - let device_id = device_id.to_owned(); let weak_ws_client = Arc::downgrade(ws_client); let weak_api_client = Arc::downgrade(api_client); af_spawn(async move { @@ -298,9 +289,9 @@ fn spawn_ws_conn( if let (Some(api_client), Some(ws_client)) = (weak_api_client.upgrade(), weak_ws_client.upgrade()) { - match api_client.ws_url(&device_id).await { - Ok(ws_addr) => { - let _ = ws_client.connect(ws_addr, &device_id).await; + match api_client.ws_connect_info().await { + Ok(conn_info) => { + let _ = ws_client.connect(&api_client.ws_addr(), conn_info).await; }, Err(err) => error!("Failed to get ws url: {}", err), } diff --git a/frontend/rust-lib/flowy-server/src/server.rs b/frontend/rust-lib/flowy-server/src/server.rs index f26676eb6c..a20fe8465a 100644 --- a/frontend/rust-lib/flowy-server/src/server.rs +++ b/frontend/rust-lib/flowy-server/src/server.rs @@ -116,7 +116,7 @@ pub trait AppFlowyServer: Send + Sync + 'static { } fn get_ws_state(&self) -> ConnectState { - ConnectState::Closed + ConnectState::Lost } #[allow(clippy::type_complexity)] diff --git a/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs b/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs index 8377eb3dd9..1a39d704a3 100644 --- a/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs +++ b/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs @@ -30,7 +30,7 @@ pub fn af_cloud_server(config: AFCloudConfiguration) -> Arc config, true, fake_device_id, - "flowy-server-test", + "0.5.1", )) } @@ -51,7 +51,7 @@ pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguratio client.gotrue_url(), "fake_device_id", ClientConfiguration::default(), - &client.client_id, + &client.client_version.to_string(), ); admin_client .sign_in_password(&admin_email, &admin_password) diff --git a/frontend/rust-lib/flowy-user/src/services/db.rs b/frontend/rust-lib/flowy-user/src/services/db.rs index 853365c04a..3305fca41a 100644 --- a/frontend/rust-lib/flowy-user/src/services/db.rs +++ b/frontend/rust-lib/flowy-user/src/services/db.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use std::{collections::HashMap, fs, io, sync::Arc, time::Duration}; -use chrono::Local; +use chrono::{Days, Local}; use collab_integrate::{CollabKVAction, CollabKVDB, PersistenceError}; use collab_plugins::local_storage::kv::KVTransactionDB; use flowy_error::FlowyError; @@ -323,40 +323,46 @@ impl CollabDBZipBackup { fn clean_old_backups(&self) -> io::Result<()> { let mut backups = Vec::new(); - let threshold_date = Local::now() - chrono::Duration::days(10); - - // Collect all backup files - for entry in fs::read_dir(&self.history_folder)? { - let entry = entry?; - let path = entry.path(); - if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("zip") { - let filename = path - .file_stem() - .and_then(|s| s.to_str()) - .unwrap_or_default(); - let date_str = filename.split('_').last().unwrap_or(""); - backups.push((date_str.to_string(), path)); - } - } - - // Sort backups by date (oldest first) - backups.sort_by(|a, b| a.0.cmp(&b.0)); - - // Remove backups older than 10 days - let threshold_str = threshold_date.format(zip_time_format()).to_string(); - - info!("Current backup: {:?}", backups.len()); - // If there are more than 10 backups, remove the oldest ones - while backups.len() > 10 { - if let Some((date_str, path)) = backups.first() { - if date_str < &threshold_str { - info!("Remove old backup file: {:?}", path); - fs::remove_file(path)?; - backups.remove(0); - } else { - break; + let now = Local::now(); + match now.checked_sub_days(Days::new(10)) { + None => { + error!("Failed to calculate threshold date"); + }, + Some(threshold_date) => { + // Collect all backup files + for entry in fs::read_dir(&self.history_folder)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("zip") { + let filename = path + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or_default(); + let date_str = filename.split('_').last().unwrap_or(""); + backups.push((date_str.to_string(), path)); + } } - } + + // Sort backups by date (oldest first) + backups.sort_by(|a, b| a.0.cmp(&b.0)); + + // Remove backups older than 10 days + let threshold_str = threshold_date.format(zip_time_format()).to_string(); + + info!("Current backup: {:?}", backups.len()); + // If there are more than 10 backups, remove the oldest ones + while backups.len() > 10 { + if let Some((date_str, path)) = backups.first() { + if date_str < &threshold_str { + info!("Remove old backup file: {:?}", path); + fs::remove_file(path)?; + backups.remove(0); + } else { + break; + } + } + } + }, } Ok(()) From 0f13f8691716e5abc30a62eb0dfc2f16819d6d5e Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 7 Mar 2024 12:56:56 +0800 Subject: [PATCH 12/13] chore: update editor version (#4836) --- frontend/appflowy_flutter/pubspec.lock | 10 +++++----- frontend/appflowy_flutter/pubspec.yaml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 56b6597c34..6d54057faf 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -53,17 +53,17 @@ packages: dependency: "direct main" description: path: "." - ref: d4d35c0 - resolved-ref: d4d35c0d103a5d1bddf68181fcfaf9f75b0fccb5 + ref: ce391a8 + resolved-ref: ce391a8c0f492f7b5fdd8f44bbc89fc68882ff23 url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git - version: "2.3.2" + version: "2.3.3" appflowy_editor_plugins: dependency: "direct main" description: path: "." - ref: "0223cca" - resolved-ref: "0223ccabe74b86092d3f3849b69026c89df3b236" + ref: "8f238f2" + resolved-ref: "8f238f214de72e629fe2d90317518c5a0510cdc5" url: "https://github.com/LucasXu0/appflowy_editor_plugins" source: git version: "0.0.1" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 792737ea37..ec6409c9e6 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -50,7 +50,7 @@ dependencies: appflowy_editor_plugins: git: url: https://github.com/LucasXu0/appflowy_editor_plugins - ref: "0223cca" + ref: "8f238f2" appflowy_popover: path: packages/appflowy_popover @@ -167,7 +167,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "d4d35c0" + ref: "ce391a8" sheet: git: From a180cfcdc2646fc2d263a9ae9e004c654ebebd6c Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:46:09 +0800 Subject: [PATCH 13/13] chore: support publish tauri app (#4829) * chore: support publish tauri app * chore: fixed others bugs * fix: code review * fix: code review * fix: tauri ci --- .github/workflows/tauri_ci.yaml | 77 ++++----- .github/workflows/tauri_release.yml | 153 ++++++++++++++++++ frontend/appflowy_tauri/package.json | 2 +- .../appflowy_tauri/scripts/update_version.cjs | 31 ++++ .../appflowy_tauri/src-tauri/tauri.conf.json | 2 +- .../confirm_dialog/DeleteConfirmDialog.tsx | 1 + .../_shared/confirm_dialog/RenameDialog.tsx | 2 +- .../KeyboardNavigation.tsx | 13 +- .../_shared/view_title/ViewBanner.tsx | 2 +- .../_shared/view_title/ViewIcon.tsx | 3 + .../auth/get_started/GetStarted.tsx | 3 +- .../components/database/Database.hooks.ts | 4 +- .../components/database/Database.tsx | 86 ++++------ .../components/database/DatabaseTitle.tsx | 4 +- .../database/components/cell/SelectCell.tsx | 4 +- .../database_settings/DatabaseSettings.tsx | 2 +- .../database_settings/FilterSettings.tsx | 2 +- .../database_settings/SortSettings.tsx | 2 +- .../field_types/date/CustomCalendar.tsx | 1 + .../components/field_types/date/calendar.scss | 82 ++++++++++ .../select/SelectOptionModifyMenu.tsx | 13 +- .../select_cell_actions/CreateOption.tsx | 16 -- .../select_cell_actions/SearchInput.tsx | 21 +-- .../select_cell_actions/SelectCellActions.tsx | 148 ++++++++--------- .../select_cell_actions/SelectOptionItem.tsx | 4 +- .../database/components/property/Property.tsx | 8 +- .../components/sort/SortConditionSelect.tsx | 5 +- .../components/tab_bar/ViewActions.tsx | 1 + .../grid/grid_calculate/GridCalculate.tsx | 4 +- .../database/grid/grid_field/GridField.tsx | 4 + .../database/grid/grid_new_row/GridNewRow.tsx | 3 +- .../database/grid/grid_table/GridTable.tsx | 4 +- .../blocks/database/DatabaseList.hooks.ts | 11 ++ .../components/blocks/database/GridView.tsx | 17 +- .../inline_nodes/link/LinkEditContent.tsx | 4 +- .../inline_nodes/link/LinkEditPopover.tsx | 2 +- .../components/tools/_shared/ColorPicker.tsx | 2 +- .../tools/block_actions/color/Color.tsx | 6 +- .../actions/_shared/ActionButton.tsx | 2 +- .../actions/color/ColorPopover.tsx | 2 +- .../components/editor/plugins/withPasted.ts | 5 +- .../layout/bread_crumb/BreadCrumb.tsx | 45 +++--- .../layout/bread_crumb/Breadcrumb.hooks.ts | 70 ++------ .../components/layout/layout.scss | 33 +++- .../layout/nested_page/NestedPage.tsx | 16 +- .../stores/reducers/pages/async_actions.ts | 19 ++- .../appflowy_tauri/src/styles/template.css | 116 +------------ 47 files changed, 624 insertions(+), 433 deletions(-) create mode 100644 .github/workflows/tauri_release.yml create mode 100644 frontend/appflowy_tauri/scripts/update_version.cjs create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/calendar.scss delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/CreateOption.tsx diff --git a/.github/workflows/tauri_ci.yaml b/.github/workflows/tauri_ci.yaml index 7045e58aa1..8d99091aab 100644 --- a/.github/workflows/tauri_ci.yaml +++ b/.github/workflows/tauri_ci.yaml @@ -25,32 +25,33 @@ jobs: platform: [ubuntu-latest] runs-on: ${{ matrix.platform }} + + env: + CI: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Maximize build space (ubuntu only) + if: matrix.platform == 'ubuntu-latest' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + sudo docker image prune --all --force + sudo rm -rf /opt/hostedtoolcache/codeQL + sudo rm -rf ${GITHUB_WORKSPACE}/.git + sudo rm -rf $ANDROID_HOME/ndk + - name: setup node uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} - - name: Cache Rust Dependencies - uses: Swatinem/rust-cache@v2 + - name: setup pnpm + uses: pnpm/action-setup@v2 with: - key: rust-dependencies-${{ runner.os }} - workspaces: | - frontend/rust-lib - frontend/appflowy_tauri/src-tauri - - - name: Cache Node.js dependencies - uses: actions/cache@v2 - with: - path: ~/.npm - key: npm-${{ runner.os }} - - - name: Cache node_modules - uses: actions/cache@v2 - with: - path: frontend/appflowy_tauri/node_modules - key: node-modules-${{ runner.os }} + version: ${{ env.PNPM_VERSION }} - name: Install Rust toolchain id: rust_toolchain @@ -60,15 +61,23 @@ jobs: override: true profile: minimal + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: "./frontend/appflowy_tauri/src-tauri -> target" + + - name: Node_modules cache + uses: actions/cache@v2 + with: + path: frontend/appflowy_tauri/node_modules + key: node-modules-${{ runner.os }} + - name: install dependencies (windows only) if: matrix.platform == 'windows-latest' working-directory: frontend run: | - cargo install --force cargo-make cargo install --force duckscript_cli vcpkg integrate install - cargo make appflowy-tauri-deps-tools - npm install -g pnpm@${{ env.PNPM_VERSION }} - name: install dependencies (ubuntu only) if: matrix.platform == 'ubuntu-latest' @@ -76,35 +85,29 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf - cargo install --force cargo-make - cargo make appflowy-tauri-deps-tools - npm install -g pnpm@${{ env.PNPM_VERSION }} - - name: install dependencies (macOS only) - if: matrix.platform == 'macos-latest' + - name: install cargo-make working-directory: frontend run: | cargo install --force cargo-make cargo make appflowy-tauri-deps-tools - npm install -g pnpm@${{ env.PNPM_VERSION }} - - name: Build + - name: install frontend dependencies working-directory: frontend/appflowy_tauri run: | mkdir dist pnpm install cargo make --cwd .. tauri_build + + - name: frontend tests and linting + working-directory: frontend/appflowy_tauri + run: | pnpm test pnpm test:errors - - name: Check for uncommitted changes - run: | - diff_files=$(git status --porcelain) - if [ -n "$diff_files" ]; then - echo "There are uncommitted changes in the working tree. Please commit them before pushing." - exit 1 - fi - - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tauriScript: pnpm tauri + projectPath: frontend/appflowy_tauri \ No newline at end of file diff --git a/.github/workflows/tauri_release.yml b/.github/workflows/tauri_release.yml new file mode 100644 index 0000000000..e031e65ccd --- /dev/null +++ b/.github/workflows/tauri_release.yml @@ -0,0 +1,153 @@ +name: Publish Tauri Release + +on: + workflow_dispatch: + inputs: + branch: + description: 'The branch to release' + required: true + default: 'main' + version: + description: 'The version to release' + required: true + default: '0.0.0' +env: + NODE_VERSION: "18.16.0" + PNPM_VERSION: "8.5.0" + RUST_TOOLCHAIN: "1.75" + +jobs: + + publish-tauri: + permissions: + contents: write + strategy: + fail-fast: false + matrix: + settings: + - platform: windows-latest + args: "--verbose" + target: "windows-x86_64" + - platform: macos-latest + args: "--target x86_64-apple-darwin" + target: "macos-x86_64" + - platform: ubuntu-latest + args: "--target x86_64-unknown-linux-gnu" + target: "linux-x86_64" + + runs-on: ${{ matrix.settings.platform }} + + env: + CI: true + PACKAGE_PREFIX: AppFlowy_Tauri-${{ github.event.inputs.version }}-${{ matrix.settings.target }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Maximize build space (ubuntu only) + if: matrix.settings.platform == 'ubuntu-latest' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + sudo docker image prune --all --force + sudo rm -rf /opt/hostedtoolcache/codeQL + sudo rm -rf ${GITHUB_WORKSPACE}/.git + sudo rm -rf $ANDROID_HOME/ndk + + - name: setup node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: setup pnpm + uses: pnpm/action-setup@v2 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Install Rust toolchain + id: rust_toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} + override: true + profile: minimal + + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: "./frontend/appflowy_tauri/src-tauri -> target" + + - name: install dependencies (windows only) + if: matrix.settings.platform == 'windows-latest' + working-directory: frontend + run: | + cargo install --force duckscript_cli + vcpkg integrate install + + - name: install dependencies (ubuntu only) + if: matrix.settings.platform == 'ubuntu-latest' + working-directory: frontend + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf + + - name: install cargo-make + working-directory: frontend + run: | + cargo install --force cargo-make + cargo make appflowy-tauri-deps-tools + + - name: install frontend dependencies + working-directory: frontend/appflowy_tauri + run: | + mkdir dist + pnpm install + pnpm exec node scripts/update_version.cjs ${{ github.event.inputs.version }} + cargo make --cwd .. tauri_build + + - uses: tauri-apps/tauri-action@dev + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + APPLE_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.MACOS_TEAM_ID }} + APPLE_ID: ${{ secrets.MACOS_NOTARY_USER }} + APPLE_TEAM_ID: ${{ secrets.MACOS_TEAM_ID }} + APPLE_PASSWORD: ${{ secrets.MACOS_NOTARY_PWD }} + CI: true + with: + args: ${{ matrix.settings.args }} + appVersion: ${{ github.event.inputs.version }} + tauriScript: pnpm tauri + projectPath: frontend/appflowy_tauri + + - name: Upload EXE package(windows only) + uses: actions/upload-artifact@v4 + if: matrix.settings.platform == 'windows-latest' + with: + name: ${{ env.PACKAGE_PREFIX }}.exe + path: frontend/appflowy_tauri/src-tauri/target/release/bundle/nsis/AppFlowy_${{ github.event.inputs.version }}_x64-setup.exe + + - name: Upload DMG package(macos only) + uses: actions/upload-artifact@v4 + if: matrix.settings.platform == 'macos-latest' + with: + name: ${{ env.PACKAGE_PREFIX }}.dmg + path: frontend/appflowy_tauri/src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/AppFlowy_${{ github.event.inputs.version }}_x64.dmg + + - name: Upload Deb package(ubuntu only) + uses: actions/upload-artifact@v4 + if: matrix.settings.platform == 'ubuntu-latest' + with: + name: ${{ env.PACKAGE_PREFIX }}.deb + path: frontend/appflowy_tauri/src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb/app-flowy_${{ github.event.inputs.version }}_amd64.deb + + - name: Upload AppImage package(ubuntu only) + uses: actions/upload-artifact@v4 + if: matrix.settings.platform == 'ubuntu-latest' + with: + name: ${{ env.PACKAGE_PREFIX }}.AppImage + path: frontend/appflowy_tauri/src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage/app-flowy_${{ github.event.inputs.version }}_amd64.AppImage diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index 6f8ed203a0..c9f8327f83 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -79,8 +79,8 @@ "yjs": "^13.5.51" }, "devDependencies": { - "@svgr/plugin-svgo": "^8.0.1", "@tauri-apps/cli": "^1.5.6", + "@svgr/plugin-svgo": "^8.0.1", "@types/google-protobuf": "^3.15.12", "@types/is-hotkey": "^0.1.7", "@types/jest": "^29.5.3", diff --git a/frontend/appflowy_tauri/scripts/update_version.cjs b/frontend/appflowy_tauri/scripts/update_version.cjs new file mode 100644 index 0000000000..498b8c3e4f --- /dev/null +++ b/frontend/appflowy_tauri/scripts/update_version.cjs @@ -0,0 +1,31 @@ +const fs = require('fs'); +const path = require('path'); + +if (process.argv.length < 3) { + console.error('Usage: node update-tauri-version.js '); + process.exit(1); +} + +const newVersion = process.argv[2]; + +const tauriConfigPath = path.join(__dirname, '../src-tauri', 'tauri.conf.json'); + +fs.readFile(tauriConfigPath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading tauri.conf.json:', err); + return; + } + + const config = JSON.parse(data); + + config.package.version = newVersion; + + fs.writeFile(tauriConfigPath, JSON.stringify(config, null, 2), 'utf8', (err) => { + if (err) { + console.error('Error writing tauri.conf.json:', err); + return; + } + + console.log(`Tauri version updated to ${newVersion} successfully.`); + }); +}); diff --git a/frontend/appflowy_tauri/src-tauri/tauri.conf.json b/frontend/appflowy_tauri/src-tauri/tauri.conf.json index 899bbeeb41..293da0ec70 100644 --- a/frontend/appflowy_tauri/src-tauri/tauri.conf.json +++ b/frontend/appflowy_tauri/src-tauri/tauri.conf.json @@ -99,4 +99,4 @@ } ] } -} +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx index 9a2d0dd882..058335d30c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx @@ -60,6 +60,7 @@ function DeleteConfirmDialog({ open, title, onOk, onCancel, onClose, okText, can