diff --git a/.github/workflows/flutter_ci.yaml b/.github/workflows/flutter_ci.yaml
index 6cc1967ade..ebb5f2c5a2 100644
--- a/.github/workflows/flutter_ci.yaml
+++ b/.github/workflows/flutter_ci.yaml
@@ -134,6 +134,6 @@ jobs:
fail_ci_if_error: true
verbose: true
os: ${{ matrix.os }}
- attempt_limit: 5
+ attempt_limit: 20
attempt_delay: 10000
diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml
index 4ccca8f067..3ca838631e 100644
--- a/.github/workflows/integration_test.yml
+++ b/.github/workflows/integration_test.yml
@@ -29,25 +29,31 @@ concurrency:
cancel-in-progress: true
jobs:
- tests:
+ build:
if: github.event.pull_request.draft != true
strategy:
+ fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
-
+ include:
+ - os: ubuntu-latest
+ flutter_profile: development-linux-x86_64
+ target: x86_64-unknown-linux-gnu
+ - os: windows-latest
+ flutter_profile: development-windows-x86
+ target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: "stable-2022-04-07"
+ - name: Checkout source code
+ uses: actions/checkout@v2
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
+ target: ${{ matrix.target }}
override: true
profile: minimal
@@ -64,6 +70,7 @@ jobs:
prefix-key: ${{ matrix.os }}
workspaces: |
frontend/rust-lib
+ cache-all-crates: true
- uses: davidB/rust-cargo-make@v1
with:
@@ -87,30 +94,24 @@ jobs:
cargo make appflowy-flutter-deps-tools
shell: bash
- - name: Config Flutter
+ - 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
- - name: Build Test lib
+ - name: Build AppFlowy
working-directory: frontend
run: |
- if [ "$RUNNER_OS" == "Linux" ]; then
- cargo make --profile development-linux-x86_64 appflowy-dev
- elif [ "$RUNNER_OS" == "macOS" ]; then
- cargo make --profile development-mac-x86_64 appflowy-dev
- elif [ "$RUNNER_OS" == "Windows" ]; then
- cargo make --profile development-windows-x86 appflowy-dev
- fi
- shell: bash
+ cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
- - name: Run AppFlowy tests
+ - name: Run Flutter integration tests
working-directory: frontend/appflowy_flutter
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
@@ -135,5 +136,5 @@ jobs:
fail_ci_if_error: true
verbose: true
os: ${{ matrix.os }}
- attempt_limit: 5
+ attempt_limit: 20
attempt_delay: 10000
\ No newline at end of file
diff --git a/frontend/appflowy_flutter/assets/images/editor/math.svg b/frontend/appflowy_flutter/assets/images/editor/math.svg
new file mode 100644
index 0000000000..32e79a21ed
--- /dev/null
+++ b/frontend/appflowy_flutter/assets/images/editor/math.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/appflowy_flutter/assets/translations/en.json b/frontend/appflowy_flutter/assets/translations/en.json
index fb885a1f6c..fa5bdca791 100644
--- a/frontend/appflowy_flutter/assets/translations/en.json
+++ b/frontend/appflowy_flutter/assets/translations/en.json
@@ -426,6 +426,7 @@
"smartEditCouldNotFetchKey": "Could not fetch OpenAI key",
"smartEditDisabled": "Connect OpenAI in Settings",
"discardResponse": "Do you want to discard the AI responses?",
+ "createInlineMathEquation": "Create equation",
"cover": {
"changeCover": "Change Cover",
"colors": "Colors",
diff --git a/frontend/appflowy_flutter/integration_test/database_calendar_test.dart b/frontend/appflowy_flutter/integration_test/database_calendar_test.dart
index cd096db6e8..5d4561925c 100644
--- a/frontend/appflowy_flutter/integration_test/database_calendar_test.dart
+++ b/frontend/appflowy_flutter/integration_test/database_calendar_test.dart
@@ -107,7 +107,7 @@ void main() {
// Make sure that the event is edited
tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
- tester.assertNumberofEventsOnSpecificDay(2, DateTime.now());
+ tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
// Click on the event
await tester.openCalendarEvent(index: 1);
@@ -119,7 +119,7 @@ void main() {
// Check that there are 2 events
tester.assertNumberOfEventsInCalendar(2, title: 'hello world');
- tester.assertNumberofEventsOnSpecificDay(3, DateTime.now());
+ tester.assertNumberOfEventsOnSpecificDay(3, DateTime.now());
// Delete an event
await tester.openCalendarEvent(index: 1);
@@ -127,7 +127,7 @@ void main() {
// Check that there is 1 event
tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
- tester.assertNumberofEventsOnSpecificDay(2, DateTime.now());
+ tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
});
testWidgets('rescheduling events', (tester) async {
@@ -150,7 +150,7 @@ void main() {
// Make sure that the event has been rescheduled to the new date
final sameDayNextWeek = firstOfThisMonth.add(const Duration(days: 7));
tester.assertNumberOfEventsInCalendar(1);
- tester.assertNumberofEventsOnSpecificDay(1, sameDayNextWeek);
+ tester.assertNumberOfEventsOnSpecificDay(1, sameDayNextWeek);
// Delete the event
await tester.openCalendarEvent(index: 0, date: sameDayNextWeek);
@@ -162,7 +162,7 @@ void main() {
await tester.dismissRowDetailPage();
// Make sure that the event is today
- tester.assertNumberofEventsOnSpecificDay(1, today);
+ tester.assertNumberOfEventsOnSpecificDay(1, today);
// Click on the event
await tester.openCalendarEvent(index: 0);
@@ -183,7 +183,7 @@ void main() {
// Make sure that the event is edited
tester.assertNumberOfEventsInCalendar(1);
- tester.assertNumberofEventsOnSpecificDay(1, newDate);
+ tester.assertNumberOfEventsOnSpecificDay(1, newDate);
});
});
}
diff --git a/frontend/appflowy_flutter/integration_test/database_row_page_test.dart b/frontend/appflowy_flutter/integration_test/database_row_page_test.dart
index bfd5a5416b..42f291586e 100644
--- a/frontend/appflowy_flutter/integration_test/database_row_page_test.dart
+++ b/frontend/appflowy_flutter/integration_test/database_row_page_test.dart
@@ -172,9 +172,10 @@ void main() {
// Focus on the editor
final textBlock = find.byType(TextBlockComponentWidget);
await tester.tapAt(tester.getCenter(textBlock));
+ await tester.pumpAndSettle();
// Input some text
- const inputText = 'Hello world';
+ const inputText = 'Hello World';
await tester.ime.insertText(inputText);
expect(
find.textContaining(inputText, findRichText: true),
diff --git a/frontend/appflowy_flutter/integration_test/database_share_test.dart b/frontend/appflowy_flutter/integration_test/database_share_test.dart
index edb6baceaa..d6571875b9 100644
--- a/frontend/appflowy_flutter/integration_test/database_share_test.dart
+++ b/frontend/appflowy_flutter/integration_test/database_share_test.dart
@@ -10,6 +10,8 @@ void main() {
group('database', () {
testWidgets('import v0.2.0 database data', (tester) async {
await tester.openV020database();
+ // wait the database data is loaded
+ await tester.pumpAndSettle(const Duration(microseconds: 500));
// check the text cell
final textCells = ['A', 'B', 'C', 'D', 'E', '', '', '', '', ''];
diff --git a/frontend/appflowy_flutter/integration_test/document/document_test.dart b/frontend/appflowy_flutter/integration_test/document/document_create_and_delete_test.dart
similarity index 98%
rename from frontend/appflowy_flutter/integration_test/document/document_test.dart
rename to frontend/appflowy_flutter/integration_test/document/document_create_and_delete_test.dart
index cd366e75a3..c447494904 100644
--- a/frontend/appflowy_flutter/integration_test/document/document_test.dart
+++ b/frontend/appflowy_flutter/integration_test/document/document_create_and_delete_test.dart
@@ -9,7 +9,7 @@ import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
- group('document', () {
+ group('create and delete the document', () {
testWidgets('create a new document when launching app in first time',
(tester) async {
await tester.initializeAppFlowy();
diff --git a/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart b/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart
new file mode 100644
index 0000000000..8ab64d88b8
--- /dev/null
+++ b/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart
@@ -0,0 +1,22 @@
+import 'package:integration_test/integration_test.dart';
+
+import 'document_create_and_delete_test.dart'
+ as document_create_and_delete_test;
+import 'document_with_cover_image_test.dart' as document_with_cover_image_test;
+import 'document_with_database_test.dart' as document_with_database_test;
+import 'document_with_inline_math_equation_test.dart'
+ as document_with_inline_math_equation_test;
+import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
+import 'edit_document_test.dart' as document_edit_test;
+
+void startTesting() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ // Document integration tests
+ document_create_and_delete_test.main();
+ document_edit_test.main();
+ document_with_database_test.main();
+ document_with_inline_page_test.main();
+ document_with_inline_math_equation_test.main();
+ document_with_cover_image_test.main();
+}
diff --git a/frontend/appflowy_flutter/integration_test/document/cover_image_test.dart b/frontend/appflowy_flutter/integration_test/document/document_with_cover_image_test.dart
similarity index 100%
rename from frontend/appflowy_flutter/integration_test/document/cover_image_test.dart
rename to frontend/appflowy_flutter/integration_test/document/document_with_cover_image_test.dart
diff --git a/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test.dart b/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test.dart
new file mode 100644
index 0000000000..00f791cfbb
--- /dev/null
+++ b/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test.dart
@@ -0,0 +1,65 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra_ui/style_widget/button.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+
+import '../util/ime.dart';
+import '../util/util.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('inline math equation in document', () {
+ testWidgets('insert an inline math equation', (tester) async {
+ await tester.initializeAppFlowy();
+ await tester.tapGoButton();
+
+ // create a new document
+ await tester.createNewPageWithName(
+ ViewLayoutPB.Document,
+ LocaleKeys.document_plugins_createInlineMathEquation.tr(),
+ );
+
+ // tap the first line of the document
+ await tester.editor.tapLineOfEditorAt(0);
+ // insert a inline page
+ const formula = 'E = MC ^ 2';
+ await tester.ime.insertText(formula);
+ await tester.editor.updateSelection(
+ Selection.single(path: [0], startOffset: 0, endOffset: formula.length),
+ );
+
+ // tap the inline math equation button
+ final inlineMathEquationButton = find.byTooltip(
+ LocaleKeys.document_plugins_createInlineMathEquation.tr(),
+ );
+ await tester.tapButton(inlineMathEquationButton);
+
+ // expect to see the math equation block
+ final inlineMathEquation = find.byType(InlineMathEquation);
+ expect(inlineMathEquation, findsOneWidget);
+
+ // tap it and update the content
+ await tester.tapButton(inlineMathEquation);
+ final textFormField = find.descendant(
+ of: find.byType(MathInputTextField),
+ matching: find.byType(TextFormField),
+ );
+ const newFormula = 'E = MC ^ 3';
+ await tester.enterText(textFormField, newFormula);
+ await tester.tapButton(
+ find.descendant(
+ of: find.byType(MathInputTextField),
+ matching: find.byType(FlowyButton),
+ ),
+ );
+ await tester.pumpAndSettle();
+ });
+ });
+}
diff --git a/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test_1.png b/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test_1.png
new file mode 100644
index 0000000000..8795110cf9
Binary files /dev/null and b/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test_1.png differ
diff --git a/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test_2.png b/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test_2.png
new file mode 100644
index 0000000000..de3d4f6113
Binary files /dev/null and b/frontend/appflowy_flutter/integration_test/document/document_with_inline_math_equation_test_2.png differ
diff --git a/frontend/appflowy_flutter/integration_test/runner.dart b/frontend/appflowy_flutter/integration_test/runner.dart
index 8dc301ddbd..934e213842 100644
--- a/frontend/appflowy_flutter/integration_test/runner.dart
+++ b/frontend/appflowy_flutter/integration_test/runner.dart
@@ -1,23 +1,19 @@
import 'package:integration_test/integration_test.dart';
-import 'switch_folder_test.dart' as switch_folder_test;
-import 'document/document_test.dart' as document_test;
-import 'document/cover_image_test.dart' as cover_image_test;
-import 'share_markdown_test.dart' as share_markdown_test;
-import 'import_files_test.dart' as import_files_test;
-import 'document/document_with_database_test.dart'
- as document_with_database_test;
-import 'document/edit_document_test.dart' as edit_document_test;
+import 'database_calendar_test.dart' as database_calendar_test;
import 'database_cell_test.dart' as database_cell_test;
import 'database_field_test.dart' as database_field_test;
-import 'database_share_test.dart' as database_share_test;
+import 'database_filter_test.dart' as database_filter_test;
import 'database_row_page_test.dart' as database_row_page_test;
import 'database_row_test.dart' as database_row_test;
import 'database_setting_test.dart' as database_setting_test;
-import 'database_filter_test.dart' as database_filter_test;
-import 'database_view_test.dart' as database_view_test;
-import 'database_calendar_test.dart' as database_calendar_test;
+import 'database_share_test.dart' as database_share_test;
import 'database_sort_test.dart' as database_sort_test;
+import 'database_view_test.dart' as database_view_test;
+import 'document/document_test_runner.dart' as document_test_runner;
+import 'import_files_test.dart' as import_files_test;
+import 'share_markdown_test.dart' as share_markdown_test;
+import 'switch_folder_test.dart' as switch_folder_test;
/// The main task runner for all integration tests in AppFlowy.
///
@@ -33,10 +29,7 @@ void main() {
import_files_test.main();
// Document integration tests
- cover_image_test.main();
- document_test.main();
- document_with_database_test.main();
- edit_document_test.main();
+ document_test_runner.startTesting();
// Database integration tests
database_cell_test.main();
diff --git a/frontend/appflowy_flutter/integration_test/util/base.dart b/frontend/appflowy_flutter/integration_test/util/base.dart
index 0c4306cc5f..9a6a199b21 100644
--- a/frontend/appflowy_flutter/integration_test/util/base.dart
+++ b/frontend/appflowy_flutter/integration_test/util/base.dart
@@ -10,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path_provider/path_provider.dart';
+import 'package:path/path.dart' as p;
class FlowyTestContext {
FlowyTestContext({
@@ -23,7 +24,10 @@ extension AppFlowyTestBase on WidgetTester {
Future initializeAppFlowy({
// use to append after the application data directory
String? pathExtension,
+ Size windowsSize = const Size(1600, 1200),
}) async {
+ binding.setSurfaceSize(windowsSize);
+
mockHotKeyManagerHandlers();
final directory = await mockApplicationDataStorage(
pathExtension: pathExtension,
@@ -60,7 +64,7 @@ extension AppFlowyTestBase on WidgetTester {
final dir = await getTemporaryDirectory();
// Use a random uuid to avoid conflict.
- String path = '${dir.path}/appflowy_integration_test/${uuid()}';
+ String path = p.join(dir.path, 'appflowy_integration_test', uuid());
if (pathExtension != null && pathExtension.isNotEmpty) {
path = '$path/$pathExtension';
}
@@ -78,7 +82,7 @@ extension AppFlowyTestBase on WidgetTester {
Finder finder, {
int? pointer,
int buttons = kPrimaryButton,
- bool warnIfMissed = true,
+ bool warnIfMissed = false,
int milliseconds = 500,
}) async {
await tap(
@@ -123,6 +127,18 @@ extension AppFlowyTestBase on WidgetTester {
return;
}
+ Future doubleTapAt(
+ Offset location, {
+ int? pointer,
+ int buttons = kPrimaryButton,
+ int milliseconds = 500,
+ }) async {
+ await tapAt(location, pointer: pointer, buttons: buttons);
+ await pump(kDoubleTapMinTime);
+ await tapAt(location, pointer: pointer, buttons: buttons);
+ await pumpAndSettle(Duration(milliseconds: milliseconds));
+ }
+
Future doubleTapButton(
Finder finder, {
int? pointer,
@@ -130,20 +146,22 @@ extension AppFlowyTestBase on WidgetTester {
bool warnIfMissed = true,
int milliseconds = 500,
}) async {
- await tapButton(
+ await tap(
finder,
pointer: pointer,
buttons: buttons,
warnIfMissed: warnIfMissed,
- milliseconds: kDoubleTapMinTime.inMilliseconds,
);
- await tapButton(
+
+ await pump(kDoubleTapMinTime);
+
+ await tap(
finder,
- pointer: pointer,
buttons: buttons,
+ pointer: pointer,
warnIfMissed: warnIfMissed,
- milliseconds: milliseconds,
);
+ await pumpAndSettle(Duration(milliseconds: milliseconds));
}
Future wait(int milliseconds) async {
diff --git a/frontend/appflowy_flutter/integration_test/util/database_test_op.dart b/frontend/appflowy_flutter/integration_test/util/database_test_op.dart
index f8fbedc3c7..b7c73afe92 100644
--- a/frontend/appflowy_flutter/integration_test/util/database_test_op.dart
+++ b/frontend/appflowy_flutter/integration_test/util/database_test_op.dart
@@ -453,8 +453,15 @@ extension AppFlowyDatabaseTest on WidgetTester {
}
Future dismissRowDetailPage() async {
- await sendKeyEvent(LogicalKeyboardKey.escape);
+ // use tap empty area instead of clicking ESC to dismiss the row detail page
+ // sometimes, the ESC key is not working.
+ await simulateKeyEvent(LogicalKeyboardKey.escape);
await pumpAndSettle();
+ final findRowDetailPage = find.byType(RowDetailPage);
+ if (findRowDetailPage.evaluate().isNotEmpty) {
+ await tapAt(const Offset(0, 0));
+ await pumpAndSettle();
+ }
}
Future editTitleInRowDetailPage(String title) async {
@@ -1031,7 +1038,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
expect(findEvents, findsNWidgets(number));
}
- void assertNumberofEventsOnSpecificDay(
+ void assertNumberOfEventsOnSpecificDay(
int number,
DateTime date, {
String? title,
@@ -1058,8 +1065,8 @@ extension AppFlowyDatabaseTest on WidgetTester {
final todayCell = find.byWidgetPredicate(
(widget) => widget is CalendarDayCard && isSameDay(date, widget.date),
);
-
- await doubleTapButton(todayCell);
+ final location = getTopLeft(todayCell).translate(10, 10);
+ await doubleTapAt(location);
}
Future openCalendarEvent({required index, DateTime? date}) async {
diff --git a/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart b/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart
index 190b68246b..cb015709cc 100644
--- a/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart
+++ b/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart
@@ -165,4 +165,14 @@ class EditorOperations {
);
await tester.tapButton(atMenuItem);
}
+
+ /// Update the editor's selection
+ Future updateSelection(Selection selection) async {
+ final editorState = getCurrentEditorState();
+ editorState.updateSelectionWithReason(
+ selection,
+ reason: SelectionUpdateReason.uiEvent,
+ );
+ await tester.pumpAndSettle(const Duration(milliseconds: 200));
+ }
}
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart
index 1372de49de..f30063be9b 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart
@@ -49,9 +49,10 @@ class _AppFlowyEditorPageState extends State {
quoteItem,
bulletedListItem,
numberedListItem,
+ inlineMathEquationItem,
linkItem,
- textColorItem,
- highlightColorItem,
+ buildTextColorItem(),
+ buildHighlightColorItem(),
];
late final List slashMenuItems;
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart
index 1c23c0b2ba..1f80b82034 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart
@@ -1,7 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
-import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
+import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart
index 3bd66ddc81..7386aa42e9 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart
@@ -2,7 +2,7 @@ import 'dart:io';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/presentation/widgets/emoji_picker/emoji_picker.dart';
-import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
+import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
@@ -43,20 +43,6 @@ enum CoverType {
}
}
-class DocumentHeaderNodeWidgetBuilder implements NodeWidgetBuilder {
- @override
- Widget build(NodeWidgetContext context) {
- return DocumentHeaderNodeWidget(
- key: context.node.key,
- node: context.node,
- editorState: context.editorState,
- );
- }
-
- @override
- NodeValidator get nodeValidator => (_) => true;
-}
-
class DocumentHeaderNodeWidget extends StatefulWidget {
const DocumentHeaderNodeWidget({
required this.node,
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_popover.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_popover.dart
index 0216a8f56e..d24e0682b1 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_popover.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_popover.dart
@@ -1,6 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/presentation/widgets/emoji_picker/emoji_picker.dart';
-import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
+import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_menu.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_menu.dart
index 2ff4b2b1b7..9aa4523df8 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_menu.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_menu.dart
@@ -1,5 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
-import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
+import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation.dart
new file mode 100644
index 0000000000..0dc30af324
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation.dart
@@ -0,0 +1,173 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/text_input.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_math_fork/flutter_math.dart';
+import 'package:provider/provider.dart';
+
+class InlineMathEquationKeys {
+ const InlineMathEquationKeys._();
+
+ static const formula = 'formula';
+}
+
+class InlineMathEquation extends StatefulWidget {
+ const InlineMathEquation({
+ super.key,
+ required this.formula,
+ required this.node,
+ required this.index,
+ this.textStyle,
+ });
+
+ final Node node;
+ final int index;
+ final String formula;
+ final TextStyle? textStyle;
+
+ @override
+ State createState() => _InlineMathEquationState();
+}
+
+class _InlineMathEquationState extends State {
+ final popoverController = PopoverController();
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return _IgnoreParentPointer(
+ child: AppFlowyPopover(
+ controller: popoverController,
+ direction: PopoverDirection.bottomWithLeftAligned,
+ popupBuilder: (_) {
+ return MathInputTextField(
+ initialText: widget.formula,
+ onSubmit: (value) async {
+ popoverController.close();
+ if (value == widget.formula) {
+ return;
+ }
+ final editorState = context.read();
+ final transaction = editorState.transaction
+ ..formatText(widget.node, widget.index, 1, {
+ InlineMathEquationKeys.formula: value,
+ });
+ await editorState.apply(transaction);
+ },
+ );
+ },
+ offset: const Offset(0, 10),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const HSpace(2),
+ Math.tex(
+ widget.formula,
+ options: MathOptions(
+ style: MathStyle.text,
+ mathFontOptions: const FontOptions(
+ fontShape: FontStyle.italic,
+ ),
+ fontSize: 14.0,
+ color: widget.textStyle?.color ??
+ theme.colorScheme.onBackground,
+ ),
+ ),
+ const HSpace(2),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class MathInputTextField extends StatefulWidget {
+ const MathInputTextField({
+ super.key,
+ required this.initialText,
+ required this.onSubmit,
+ });
+
+ final String initialText;
+ final void Function(String value) onSubmit;
+
+ @override
+ State createState() => _MathInputTextFieldState();
+}
+
+class _MathInputTextFieldState extends State {
+ late final TextEditingController textEditingController;
+
+ @override
+ void initState() {
+ super.initState();
+
+ textEditingController = TextEditingController(
+ text: widget.initialText,
+ );
+ textEditingController.selection = TextSelection(
+ baseOffset: 0,
+ extentOffset: widget.initialText.length,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: 240,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Expanded(
+ child: FlowyFormTextInput(
+ autoFocus: true,
+ textAlign: TextAlign.left,
+ controller: textEditingController,
+ contentPadding: const EdgeInsets.symmetric(
+ vertical: 8.0,
+ horizontal: 4.0,
+ ),
+ onEditingComplete: () =>
+ widget.onSubmit(textEditingController.text),
+ ),
+ ),
+ const HSpace(4.0),
+ FlowyButton(
+ text: FlowyText(LocaleKeys.button_Done.tr()),
+ useIntrinsicWidth: true,
+ onTap: () => widget.onSubmit(textEditingController.text),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class _IgnoreParentPointer extends StatelessWidget {
+ const _IgnoreParentPointer({
+ required this.child,
+ });
+
+ final Widget child;
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ behavior: HitTestBehavior.opaque,
+ onTap: () {},
+ onTapDown: (_) {},
+ onDoubleTap: () {},
+ onLongPress: () {},
+ child: child,
+ );
+ }
+}
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation_toolbar_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation_toolbar_item.dart
new file mode 100644
index 0000000000..7df4d49385
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation_toolbar_item.dart
@@ -0,0 +1,52 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/image.dart';
+import 'package:flutter/material.dart';
+
+final ToolbarItem inlineMathEquationItem = ToolbarItem(
+ id: 'editor.inline_math_equation',
+ group: 2,
+ isActive: onlyShowInSingleSelectionAndTextType,
+ builder: (context, editorState) {
+ final selection = editorState.selection!;
+ final nodes = editorState.getNodesInSelection(selection);
+ final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
+ return delta.everyAttributes(
+ (attributes) => attributes[InlineMathEquationKeys.formula] != null,
+ );
+ });
+ return IconItemWidget(
+ iconBuilder: (_) => svgWidget(
+ 'editor/math',
+ size: const Size.square(16),
+ color: Colors.white,
+ ),
+ isHighlight: isHighlight,
+ tooltip: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
+ onPressed: () async {
+ final selection = editorState.selection;
+ if (selection == null || selection.isCollapsed) {
+ return;
+ }
+ final node = editorState.getNodeAtPath(selection.start.path);
+ if (node == null) {
+ return;
+ }
+ final text = editorState.getTextInSelection(selection).join();
+ final transaction = editorState.transaction
+ ..replaceText(
+ node,
+ selection.startIndex,
+ selection.length,
+ '\$',
+ attributes: {
+ InlineMathEquationKeys.formula: text,
+ },
+ );
+ await editorState.apply(transaction);
+ },
+ );
+ },
+);
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_page/inline_page_reference.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_page/inline_page_reference.dart
index 16e0a12e2d..acbc472e86 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_page/inline_page_reference.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/inline_page/inline_page_reference.dart
@@ -24,8 +24,6 @@ class MentionBlockKeys {
static const mention = 'mention';
static const type = 'type'; // MentionType, String
static const pageId = 'page_id';
- static const pageType = 'page_type';
- static const pageName = 'page_name';
}
class InlinePageReferenceService {
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart
index 6c3d054a98..9bb56f873a 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart
@@ -1,22 +1,24 @@
+export 'actions/block_action_list.dart';
+export 'actions/option_action.dart';
export 'callout/callout_block_component.dart';
export 'code_block/code_block_component.dart';
export 'code_block/code_block_shortcut_event.dart';
-export 'header/cover_editor_bloc.dart';
-export 'header/document_header_node_widget.dart';
-export 'header/custom_cover_picker.dart';
-export 'emoji_picker/emoji_menu_item.dart';
-export 'extensions/flowy_tint_extension.dart';
+export 'database/database_view_block_component.dart';
export 'database/inline_database_menu_item.dart';
export 'database/referenced_database_menu_item.dart';
-export 'database/database_view_block_component.dart';
+export 'emoji_picker/emoji_menu_item.dart';
+export 'extensions/flowy_tint_extension.dart';
+export 'header/cover_editor_bloc.dart';
+export 'header/custom_cover_picker.dart';
+export 'header/document_header_node_widget.dart';
+export 'image/image_menu.dart';
+export 'image/image_selection_menu.dart';
+export 'inline_math_equation/inline_math_equation.dart';
+export 'inline_math_equation/inline_math_equation_toolbar_item.dart';
export 'math_equation/math_equation_block_component.dart';
export 'openai/widgets/auto_completion_node_widget.dart';
export 'openai/widgets/smart_edit_node_widget.dart';
export 'openai/widgets/smart_edit_toolbar_item.dart';
+export 'outline/outline_block_component.dart';
export 'toggle/toggle_block_component.dart';
export 'toggle/toggle_block_shortcut_event.dart';
-export 'outline/outline_block_component.dart';
-export 'image/image_menu.dart';
-export 'image/image_selection_menu.dart';
-export 'actions/option_action.dart';
-export 'actions/block_action_list.dart';
diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart
index a52589c735..12895a3e1d 100644
--- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart
+++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart
@@ -1,7 +1,8 @@
+import 'package:appflowy/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/inline_page/inline_page_reference.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
-import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg, Log;
+import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -173,13 +174,17 @@ class EditorStyleCustomizer {
}
InlineSpan customizeAttributeDecorator(
- TextInsert textInsert,
+ Node node,
+ int index,
+ TextInsert text,
TextSpan textSpan,
) {
- final attributes = textInsert.attributes;
+ final attributes = text.attributes;
if (attributes == null) {
return textSpan;
}
+
+ // customize the inline mention block, like inline page
final mention = attributes[MentionBlockKeys.mention] as Map?;
if (mention != null) {
final type = mention[MentionBlockKeys.type];
@@ -193,6 +198,21 @@ class EditorStyleCustomizer {
);
}
}
+
+ // customize the inline math equation block
+ final formula = attributes[InlineMathEquationKeys.formula] as String?;
+ if (formula != null) {
+ return WidgetSpan(
+ alignment: PlaceholderAlignment.middle,
+ child: InlineMathEquation(
+ node: node,
+ index: index,
+ formula: formula,
+ textStyle: style().textStyleConfiguration.text,
+ ),
+ );
+ }
+
return textSpan;
}
}
diff --git a/frontend/appflowy_flutter/lib/startup/startup.dart b/frontend/appflowy_flutter/lib/startup/startup.dart
index 877345123b..e2a0e76d6d 100644
--- a/frontend/appflowy_flutter/lib/startup/startup.dart
+++ b/frontend/appflowy_flutter/lib/startup/startup.dart
@@ -46,8 +46,9 @@ class FlowyRunner {
final launcher = getIt();
launcher.addTasks(
[
- // handle platform errors.
- const PlatformErrorCatcherTask(),
+ // this task should be first task, for handling platform errors.
+ // don't catch errors in test mode
+ if (!mode.isUnitTest) const PlatformErrorCatcherTask(),
// localization
const InitLocalizationTask(),
// init the app window
diff --git a/frontend/appflowy_flutter/lib/startup/tasks/windows.dart b/frontend/appflowy_flutter/lib/startup/tasks/windows.dart
index c2d044e631..e3fa0a189b 100644
--- a/frontend/appflowy_flutter/lib/startup/tasks/windows.dart
+++ b/frontend/appflowy_flutter/lib/startup/tasks/windows.dart
@@ -19,17 +19,14 @@ class InitAppWindowTask extends LaunchTask with WindowListener {
@override
Future initialize(LaunchContext context) async {
// Don't initialize on mobile or web.
- if (!defaultTargetPlatform.isDesktop) {
+ if (!defaultTargetPlatform.isDesktop || context.env.isIntegrationTest) {
return;
}
await windowManager.ensureInitialized();
windowManager.addListener(this);
- Size windowSize = await WindowSizeManager().getSize();
- if (context.env.isIntegrationTest) {
- windowSize = const Size(1600, 1200);
- }
+ final windowSize = await WindowSizeManager().getSize();
final windowOptions = WindowOptions(
size: windowSize,
diff --git a/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart
index f586ce4005..1c33008b34 100644
--- a/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart
+++ b/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart
@@ -96,7 +96,7 @@ class SplashScreen extends StatelessWidget {
}
Future _registerIfNeeded() async {
- final result = await UserEventCheckUser().send();
+ final result = await UserEventGetUserProfile().send();
if (!result.isLeft()) {
await getIt().signUpAsGuest();
}
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 39f95695ae..41963bdc34 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
@@ -98,6 +98,7 @@ class MockApplicationDataStorage extends ApplicationDataStorage {
final path = initialPath;
if (path != null) {
initialPath = null;
+ await super.setPath(path);
return Future.value(path);
}
return super.getPath();
diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart
index 6a0c3b8d3b..6e4dd11943 100644
--- a/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart
+++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart
@@ -5,16 +5,23 @@ import 'package:flutter_svg/flutter_svg.dart';
///
/// Get the hover color from ThemeData
class FlowySvg extends StatelessWidget {
- const FlowySvg({super.key, this.size, required this.name});
+ const FlowySvg({
+ super.key,
+ required this.name,
+ this.size,
+ this.color,
+ });
+
final String name;
final Size? size;
+ final Color? color;
@override
Widget build(BuildContext context) {
return svgWidget(
name,
size: size,
- color: Theme.of(context).iconTheme.color,
+ color: color ?? Theme.of(context).iconTheme.color,
);
}
}
diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock
index 684f8c01b1..39c56c42a2 100644
--- a/frontend/appflowy_flutter/pubspec.lock
+++ b/frontend/appflowy_flutter/pubspec.lock
@@ -53,11 +53,11 @@ packages:
dependency: "direct main"
description:
path: "."
- ref: "572a174"
- resolved-ref: "572a174892267e2f78f9c3d7f1fe4ca71c9be0db"
+ ref: c5b5e64
+ resolved-ref: c5b5e641fe11ae634f02db112e71f40a119e9c44
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
- version: "1.0.4"
+ version: "1.1.0"
appflowy_popover:
dependency: "direct main"
description:
diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml
index 2cda3a2c96..2962dced30 100644
--- a/frontend/appflowy_flutter/pubspec.yaml
+++ b/frontend/appflowy_flutter/pubspec.yaml
@@ -46,7 +46,7 @@ dependencies:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
- ref: 572a174
+ ref: c5b5e64
appflowy_popover:
path: packages/appflowy_popover
diff --git a/frontend/appflowy_tauri/src-tauri/src/main.rs b/frontend/appflowy_tauri/src-tauri/src/main.rs
index ecee588521..edc59ed240 100644
--- a/frontend/appflowy_tauri/src-tauri/src/main.rs
+++ b/frontend/appflowy_tauri/src-tauri/src/main.rs
@@ -7,7 +7,7 @@ mod init;
mod notification;
mod request;
-use flowy_notification::register_notification_sender;
+use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
use init::*;
use notification::*;
use request::*;
@@ -22,6 +22,8 @@ fn main() {
.on_menu_event(|_menu| {})
.on_page_load(|window, _payload| {
let app_handler = window.app_handle();
+ // Make sure hot reload won't register the notification sender twice
+ unregister_all_notification_sender();
register_notification_sender(TSNotificationSender::new(app_handler.clone()));
// tauri::async_runtime::spawn(async move {});
window.listen_global(AF_EVENT, move |event| {
diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock
index 8d9ebca719..a97f55a076 100644
--- a/frontend/rust-lib/Cargo.lock
+++ b/frontend/rust-lib/Cargo.lock
@@ -85,6 +85,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "appflowy-integrate"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"anyhow",
"collab",
@@ -896,6 +897,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"anyhow",
"bytes",
@@ -913,6 +915,7 @@ dependencies = [
[[package]]
name = "collab-client-ws"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"bytes",
"collab-sync",
@@ -930,6 +933,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"anyhow",
"async-trait",
@@ -956,6 +960,7 @@ dependencies = [
[[package]]
name = "collab-derive"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"proc-macro2",
"quote",
@@ -967,6 +972,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"anyhow",
"collab",
@@ -985,6 +991,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"anyhow",
"chrono",
@@ -1004,6 +1011,7 @@ dependencies = [
[[package]]
name = "collab-persistence"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"bincode",
"chrono",
@@ -1023,6 +1031,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"anyhow",
"async-trait",
@@ -1056,6 +1065,7 @@ dependencies = [
[[package]]
name = "collab-sync"
version = "0.1.0"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2134c0#2134c0f27b8a9f3077e25ae928f2420c926506cc"
dependencies = [
"bytes",
"collab",
diff --git a/frontend/rust-lib/dart-ffi/src/lib.rs b/frontend/rust-lib/dart-ffi/src/lib.rs
index 45e201c228..b249a2756c 100644
--- a/frontend/rust-lib/dart-ffi/src/lib.rs
+++ b/frontend/rust-lib/dart-ffi/src/lib.rs
@@ -6,7 +6,7 @@ use lazy_static::lazy_static;
use parking_lot::RwLock;
use flowy_core::*;
-use flowy_notification::register_notification_sender;
+use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
use lib_dispatch::prelude::ToBytes;
use lib_dispatch::prelude::*;
@@ -90,6 +90,8 @@ pub extern "C" fn sync_event(input: *const u8, len: usize) -> *const u8 {
#[no_mangle]
pub extern "C" fn set_stream_port(port: i64) -> i32 {
+ // Make sure hot reload won't register the notification sender twice
+ unregister_all_notification_sender();
register_notification_sender(DartNotificationSender::new(port));
0
}
diff --git a/frontend/rust-lib/flowy-notification/src/lib.rs b/frontend/rust-lib/flowy-notification/src/lib.rs
index 5ce28e68c9..9d425f968c 100644
--- a/frontend/rust-lib/flowy-notification/src/lib.rs
+++ b/frontend/rust-lib/flowy-notification/src/lib.rs
@@ -14,6 +14,10 @@ lazy_static! {
static ref NOTIFICATION_SENDER: RwLock>> = RwLock::new(vec![]);
}
+/// Register a notification sender. The sender will be alive until the process exits.
+/// Flutter integration test or Tauri hot reload might cause register multiple times.
+/// So before register a new sender, you might need to unregister the old one. Currently,
+/// Just remove all senders by calling `unregister_all_notification_sender`.
pub fn register_notification_sender(sender: T) {
let box_sender = Box::new(sender);
match NOTIFICATION_SENDER.write() {
@@ -22,6 +26,13 @@ pub fn register_notification_sender(sender: T) {
}
}
+pub fn unregister_all_notification_sender() {
+ match NOTIFICATION_SENDER.write() {
+ Ok(mut write_guard) => write_guard.clear(),
+ Err(err) => tracing::error!("Failed to remove all notification senders: {:?}", err),
+ }
+}
+
pub trait NotificationSender: Send + Sync + 'static {
fn send_subject(&self, subject: SubscribeObject) -> Result<(), String>;
}
diff --git a/frontend/scripts/code_generation/freezed/generate_freezed.sh b/frontend/scripts/code_generation/freezed/generate_freezed.sh
old mode 100644
new mode 100755
index f4c84d910e..01f578d3dc
--- a/frontend/scripts/code_generation/freezed/generate_freezed.sh
+++ b/frontend/scripts/code_generation/freezed/generate_freezed.sh
@@ -10,12 +10,14 @@ cd ../../../appflowy_flutter
# Navigate to the appflowy_flutter directory and generate files
echo "Generating files for appflowy_flutter"
-flutter clean >/dev/null 2>&1 && flutter packages pub get >/dev/null 2>&1 && dart run build_runner clean && dart run build_runner build -d
+# flutter clean >/dev/null 2>&1 && flutter packages pub get >/dev/null 2>&1 && dart run build_runner clean &&
+flutter packages pub get >/dev/null 2>&1
+dart run build_runner build -d
echo "Done generating files for appflowy_flutter"
echo "Generating files for packages"
cd packages
-for d in */ ; do
+for d in */; do
# Navigate into the subdirectory
cd "$d"
@@ -23,7 +25,8 @@ for d in */ ; do
if [ -f "pubspec.yaml" ]; then
echo "Generating freezed files in $d..."
echo "Please wait while we clean the project and fetch the dependencies."
- flutter clean >/dev/null 2>&1 && flutter packages pub get >/dev/null 2>&1 && dart run build_runner clean && dart run build_runner build -d
+ flutter packages pub get >/dev/null 2>&1
+ dart run build_runner build -d
echo "Done running build command in $d"
else
echo "No pubspec.yaml found in $d, it can\'t be a Dart project. Skipping."
@@ -34,4 +37,4 @@ for d in */ ; do
done
# Return to the original directory
-cd "$original_dir"
\ No newline at end of file
+cd "$original_dir"
diff --git a/frontend/scripts/code_generation/generate.sh b/frontend/scripts/code_generation/generate.sh
old mode 100644
new mode 100755
diff --git a/frontend/scripts/code_generation/language_files/generate_language_files.cmd b/frontend/scripts/code_generation/language_files/generate_language_files.cmd
index 984f4f365d..146e8188e5 100644
--- a/frontend/scripts/code_generation/language_files/generate_language_files.cmd
+++ b/frontend/scripts/code_generation/language_files/generate_language_files.cmd
@@ -10,8 +10,6 @@ cd /d "%~dp0"
cd ..\..\..\appflowy_flutter
-call flutter clean
-
call flutter packages pub get
echo Specifying source directory for AppFlowy Localizations.
diff --git a/frontend/scripts/code_generation/language_files/generate_language_files.sh b/frontend/scripts/code_generation/language_files/generate_language_files.sh
old mode 100644
new mode 100755
index 41e37eb268..dc624eb889
--- a/frontend/scripts/code_generation/language_files/generate_language_files.sh
+++ b/frontend/scripts/code_generation/language_files/generate_language_files.sh
@@ -10,8 +10,6 @@ cd "$(dirname "$0")"
# Navigate to the project root
cd ../../../appflowy_flutter
-flutter clean
-
flutter packages pub get
echo "Specifying source directory for AppFlowy Localizations."
diff --git a/frontend/scripts/makefile/flutter.toml b/frontend/scripts/makefile/flutter.toml
index 135fbaa66d..a2e3646536 100644
--- a/frontend/scripts/makefile/flutter.toml
+++ b/frontend/scripts/makefile/flutter.toml
@@ -167,7 +167,6 @@ script = [
[tasks.flutter-build]
script = ["""
cd appflowy_flutter/
- flutter clean
flutter pub get
flutter build ${TARGET_OS} --${BUILD_FLAG}
"""]
@@ -176,7 +175,6 @@ script_runner = "@shell"
[tasks.flutter-build.windows]
script = ["""
cd appflowy_flutter
- exec cmd.exe /c flutter clean
exec cmd.exe /c flutter pub get
exec cmd.exe /c flutter build ${TARGET_OS} --${BUILD_FLAG} --build-name=${APP_VERSION}
"""]
@@ -186,16 +184,15 @@ script_runner = "@duckscript"
script_runner = "@shell"
script = [
"""
- chmod +x scripts/code_generation/generate.sh
- """,
- "scripts/code_generation/generate.sh"
+ sh scripts/code_generation/generate.sh
+ """
]
[tasks.code_generation.windows]
script_runner = "@duckscript"
script = [
"""
- exec "scripts/code_generation/generate.cmd"
+ exec scripts/code_generation/generate.cmd
""",
]