From 1536cdd15a5cfc94475bb77a4c27c35c2e228991 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 24 Mar 2023 16:57:35 +0800 Subject: [PATCH 01/37] feat: trigger shortcut event by character (#2101) --- .../lib/src/service/keyboard_service.dart | 9 ++- .../built_in_shortcut_events.dart | 4 +- .../shortcut_event/shortcut_event.dart | 27 ++++++-- frontend/appflowy_flutter/pubspec.lock | 66 +++++++++---------- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart index b903fed4f8..2068a2a0f0 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart @@ -129,7 +129,7 @@ class _AppFlowyKeyboardState extends State // TODO: use cache to optimize the searching time. for (final shortcutEvent in widget.shortcutEvents) { - if (shortcutEvent.keybindings.containsKeyEvent(event)) { + if (shortcutEvent.canRespondToRawKeyEvent(event)) { final result = shortcutEvent.handler(widget.editorState, event); if (result == KeyEventResult.handled) { return KeyEventResult.handled; @@ -157,3 +157,10 @@ class _AppFlowyKeyboardState extends State return onKey(event); } } + +extension on ShortcutEvent { + bool canRespondToRawKeyEvent(RawKeyEvent event) { + return ((character?.isNotEmpty ?? false) && character == event.character) || + keybindings.containsKeyEvent(event); + } +} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart index b4566acf92..825dc0b297 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -247,7 +247,7 @@ List builtInShortcutEvents = [ ), ShortcutEvent( key: 'selection menu', - command: 'slash,shift+slash', + character: '/', handler: slashShortcutHandler, ), ShortcutEvent( @@ -304,7 +304,7 @@ List builtInShortcutEvents = [ ), ShortcutEvent( key: 'Underscore to italic', - command: 'shift+underscore', + character: '_', handler: underscoreToItalicHandler, ), ShortcutEvent( diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart index fb1a245b00..bba5aff1c3 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart @@ -8,12 +8,29 @@ import 'package:flutter/foundation.dart'; class ShortcutEvent { ShortcutEvent({ required this.key, - required this.command, + this.character, + this.command, required this.handler, String? windowsCommand, String? macOSCommand, String? linuxCommand, }) { + // character and command cannot be null at the same time + assert( + !(character == null && + command == null && + windowsCommand == null && + macOSCommand == null && + linuxCommand == null), + 'character and command cannot be null at the same time'); + assert( + !(character != null && + (command != null && + windowsCommand != null && + macOSCommand != null && + linuxCommand != null)), + 'character and command cannot be set at the same time'); + updateCommand( command: command, windowsCommand: windowsCommand, @@ -43,7 +60,9 @@ class ShortcutEvent { /// /// Like, 'ctrl+c,cmd+c' /// - String command; + String? command; + + String? character; final ShortcutEventHandler handler; @@ -80,9 +99,9 @@ class ShortcutEvent { matched = true; } - if (matched) { + if (matched && this.command != null) { _keybindings = this - .command + .command! .split(',') .map((e) => Keybinding.parse(e)) .toList(growable: false); diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index f880223728..f6e21acf11 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -121,7 +121,7 @@ packages: name: build_daemon url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.1" build_resolvers: dependency: transitive description: @@ -156,7 +156,7 @@ packages: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.4.3" + version: "8.4.4" calendar_view: dependency: "direct main" description: @@ -500,7 +500,7 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.0.9" flutter_svg: dependency: transitive description: @@ -524,7 +524,7 @@ packages: name: fluttertoast url: "https://pub.dartlang.org" source: hosted - version: "8.1.2" + version: "8.2.1" freezed: dependency: "direct dev" description: @@ -599,7 +599,7 @@ packages: name: html url: "https://pub.dartlang.org" source: hosted - version: "0.15.1" + version: "0.15.2" http: dependency: "direct main" description: @@ -709,7 +709,7 @@ packages: name: logger url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.3.0" logging: dependency: transitive description: @@ -779,7 +779,7 @@ packages: name: node_preamble url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" package_config: dependency: transitive description: @@ -856,35 +856,35 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.14" path_provider_android: dependency: transitive description: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.22" + version: "2.0.24" path_provider_foundation: dependency: transitive description: name: path_provider_foundation url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.2.0" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.8" + version: "2.1.10" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.6" path_provider_windows: dependency: transitive description: @@ -898,7 +898,7 @@ packages: name: percent_indicator url: "https://pub.dartlang.org" source: hosted - version: "4.2.2" + version: "4.2.3" petitparser: dependency: transitive description: @@ -919,7 +919,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" pool: dependency: transitive description: @@ -1024,7 +1024,7 @@ packages: name: rich_clipboard_windows url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" screen_retriever: dependency: transitive description: @@ -1038,49 +1038,49 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.17" + version: "2.0.20" shared_preferences_android: dependency: transitive description: name: shared_preferences_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.15" + version: "2.0.17" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.5" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.5" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.6" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.5" shelf: dependency: transitive description: @@ -1122,7 +1122,7 @@ packages: name: sized_context url: "https://pub.dartlang.org" source: hosted - version: "1.0.0+1" + version: "1.0.0+4" sky_engine: dependency: transitive description: flutter @@ -1155,7 +1155,7 @@ packages: name: source_maps url: "https://pub.dartlang.org" source: hosted - version: "0.10.11" + version: "0.10.12" source_span: dependency: transitive description: @@ -1295,56 +1295,56 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.1.9" + version: "6.1.10" url_launcher_android: dependency: transitive description: name: url_launcher_android url: "https://pub.dartlang.org" source: hosted - version: "6.0.23" + version: "6.0.26" url_launcher_ios: dependency: transitive description: name: url_launcher_ios url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.1.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.0.4" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.0.4" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.14" + version: "2.0.16" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "3.0.3" + version: "3.0.5" uuid: dependency: transitive description: @@ -1407,7 +1407,7 @@ packages: name: window_manager url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.3.1" xdg_directories: dependency: transitive description: From f9a1cb26233eeb7d80a6354913dc68ed279d2975 Mon Sep 17 00:00:00 2001 From: Mihir <84044317+squidrye@users.noreply.github.com> Date: Mon, 27 Mar 2023 07:50:01 +0530 Subject: [PATCH 02/37] fix: open-ai replace does not work in certain use-cases (#2100) * test: added test to verify correct ordering after replacement of multiline text-nodes * fix: open-ai replace does not work on certain use-cases * refactor: using predefined operation insert node to create new nodes. * Revert "refactor: using predefined operation insert node to create new nodes." This reverts commit bcc014e84d09633ee14d5090f06e609fa95af481. * refactor: using predefined operation insert node to create new nodes. * fix: open-ai replace does not work in certain use-cases * fix: fixed logic and tests for replacement of larger textNodes with smaller text. --------- Co-authored-by: Lucas.Xu --- .../lib/src/core/transform/transaction.dart | 45 ++++++++++++++++--- .../test/core/transform/transaction_test.dart | 39 +++++++++++++++- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart index 15bce2a6b7..6c369f566e 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart @@ -363,6 +363,19 @@ extension TextTransaction on Transaction { ); } else { deleteNode(textNode); + if (i == textNodes.length - 1) { + final delta = Delta() + ..insert(texts[0]) + ..addAll( + textNodes.last.delta.slice(selection.end.offset), + ); + replaceText( + textNode, + selection.start.offset, + texts[0].length, + delta.toPlainText(), + ); + } } } afterSelection = null; @@ -371,6 +384,8 @@ extension TextTransaction on Transaction { if (textNodes.length < texts.length) { final length = texts.length; + var path = textNodes.first.path; + for (var i = 0; i < texts.length; i++) { final text = texts[i]; if (i == 0) { @@ -380,13 +395,15 @@ extension TextTransaction on Transaction { textNodes.first.toPlainText().length, text, ); - } else if (i == length - 1) { + path = path.next; + } else if (i == length - 1 && textNodes.length >= 2) { replaceText( textNodes.last, 0, selection.endIndex, text, ); + path = path.next; } else { if (i < textNodes.length - 1) { replaceText( @@ -395,14 +412,28 @@ extension TextTransaction on Transaction { textNodes[i].toPlainText().length, text, ); + path = path.next; } else { - var path = textNodes.first.path; - var j = i - textNodes.length + length - 1; - while (j > 0) { - path = path.next; - j--; + if (i == texts.length - 1) { + final delta = Delta() + ..insert(text) + ..addAll( + textNodes.last.delta.slice(selection.end.offset), + ); + insertNode( + path, + TextNode( + delta: delta, + ), + ); + } else { + insertNode( + path, + TextNode( + delta: Delta()..insert(text), + ), + ); } - insertNode(path, TextNode(delta: Delta()..insert(text))); } } } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart index 8aa53dcabc..bfb9fa2b3a 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart @@ -125,7 +125,7 @@ void main() async { .map((e) => editor.nodeAtPath([e])!) .whereType() .toList(growable: false); - expect(textNodes[0].toPlainText(), '0123ABC'); + expect(textNodes[0].toPlainText(), '0123ABC456789'); }); testWidgets('test replaceTexts, textNodes.length < texts.length', @@ -165,5 +165,42 @@ void main() async { expect(textNodes[2].toPlainText(), 'ABC'); expect(textNodes[3].toPlainText(), 'ABC456789'); }); + + testWidgets('test replaceTexts, textNodes.length << texts.length', + (tester) async { + TestWidgetsFlutterBinding.ensureInitialized(); + + final editor = tester.editor..insertTextNode('Welcome to AppFlowy!'); + await editor.startTesting(); + await tester.pumpAndSettle(); + + expect(editor.documentLength, 1); + + // select 'to' + final selection = Selection( + start: Position(path: [0], offset: 8), + end: Position(path: [0], offset: 10), + ); + final transaction = editor.editorState.transaction; + var textNodes = [0] + .map((e) => editor.nodeAtPath([e])!) + .whereType() + .toList(growable: false); + final texts = ['ABC1', 'ABC2', 'ABC3', 'ABC4', 'ABC5']; + transaction.replaceTexts(textNodes, selection, texts); + editor.editorState.apply(transaction); + await tester.pumpAndSettle(); + + expect(editor.documentLength, 5); + textNodes = [0, 1, 2, 3, 4] + .map((e) => editor.nodeAtPath([e])!) + .whereType() + .toList(growable: false); + expect(textNodes[0].toPlainText(), 'Welcome ABC1'); + expect(textNodes[1].toPlainText(), 'ABC2'); + expect(textNodes[2].toPlainText(), 'ABC3'); + expect(textNodes[3].toPlainText(), 'ABC4'); + expect(textNodes[4].toPlainText(), 'ABC5 AppFlowy!'); + }); }); } From f40d1a9a963a784a934667019a19835f0281a322 Mon Sep 17 00:00:00 2001 From: Om Gujarathi <98649066+Om-Gujarathi@users.noreply.github.com> Date: Mon, 27 Mar 2023 07:50:21 +0530 Subject: [PATCH 03/37] fix: double asterisk to bold fixes #2080 (#2093) * fix: double asterisk to bold fixed * double asterisk to bold fixed --- .../src/service/shortcut_event/built_in_shortcut_events.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart index 825dc0b297..d9a0470f3d 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -309,7 +309,7 @@ List builtInShortcutEvents = [ ), ShortcutEvent( key: 'Double asterisk to bold', - command: 'shift+digit 8', + character: '*', handler: doubleAsteriskToBoldHandler, ), ShortcutEvent( From 5afdb5de354c3171eadef64366f9c2267743cb78 Mon Sep 17 00:00:00 2001 From: Akheel Muhammed <73631606+not-shoyo@users.noreply.github.com> Date: Mon, 27 Mar 2023 10:17:22 +0530 Subject: [PATCH 04/37] Fix: #2028 auto enter edit mode for text field. (#2096) * chore(): initial commit to make AppFlowy Work Signed-off-by: not-shoyo * fix: #2028, fix auto-entering edit mode Make text field request for focus when popover changes. Signed-off-by: not-shoyo * Update section.dart * Update input_service.dart * Update input_service.dart * Update input_service.dart --------- Signed-off-by: not-shoyo --- .../grid/presentation/widgets/header/field_editor.dart | 2 ++ .../workspace/presentation/home/menu/app/section/section.dart | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart index 29e38e7a72..ff0abf92af 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart @@ -147,6 +147,8 @@ class _FieldNameTextFieldState extends State<_FieldNameTextField> { widget.popoverMutex.listenOnPopoverChanged(() { if (focusNode.hasFocus) { focusNode.unfocus(); + } else { + focusNode.requestFocus(); } }); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart index b16d9b0e57..c134ca8985 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart @@ -58,7 +58,6 @@ class ViewSection extends StatelessWidget { .read() .add(ViewSectionEvent.moveView(oldIndex, index)); }, - ignorePrimaryScrollController: true, children: children, ); } From c73b7d1184be2ed440d5a9a6f9c98d6f013179bd Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Mon, 27 Mar 2023 06:47:41 +0200 Subject: [PATCH 05/37] fix: allow # in links when pasting (#2110) Closes: #1871 --- .../service/internal_key_event_handlers/copy_paste_handler.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart index 3110a0e559..2a72400b1d 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart @@ -232,7 +232,7 @@ void _pasteSingleLine( /// parse url from the line text /// reference: https://stackoverflow.com/questions/59444837/flutter-dart-regex-to-extract-urls-from-a-string Delta _lineContentToDelta(String lineContent) { - final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+'); + final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\#\w/\-?=%.]+'); final Iterable matches = exp.allMatches(lineContent); final delta = Delta(); From d62493ce012feca7a26ec8903387703b0a0f88d3 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Mon, 27 Mar 2023 06:48:07 +0200 Subject: [PATCH 06/37] fix: dropdown state unchanging after file reset (#2109) --- .../lib/workspace/application/appearance.dart | 4 ++-- .../settings/widgets/settings_language_view.dart | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/appearance.dart index b5c3ecb9bf..9cadf7f5ce 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/appearance.dart @@ -55,9 +55,9 @@ class AppearanceSettingsCubit extends Cubit { newLocale = const Locale('en'); } - context.setLocale(newLocale); - if (state.locale != newLocale) { + context.setLocale(newLocale); + _setting.locale.languageCode = newLocale.languageCode; _setting.locale.countryCode = newLocale.countryCode ?? ""; _saveAppearanceSettings(); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart index 20cb1eaafa..7b76aa6157 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart @@ -54,8 +54,8 @@ class _LanguageSelectorDropdownState extends State { @override Widget build(BuildContext context) { return MouseRegion( - onEnter: (event) => {hoverEnterLanguage()}, - onExit: (event) => {hoverExitLanguage()}, + onEnter: (_) => hoverEnterLanguage(), + onExit: (_) => hoverExitLanguage(), child: Container( margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( @@ -67,12 +67,10 @@ class _LanguageSelectorDropdownState extends State { padding: const EdgeInsets.symmetric(horizontal: 6), child: DropdownButton( value: context.locale, - onChanged: (val) { - setState(() { - context - .read() - .setLocale(context, val!); - }); + onChanged: (locale) { + context + .read() + .setLocale(context, locale!); }, autofocus: true, borderRadius: BorderRadius.circular(8), From 2a55febe623ae84ad9c58fe20bcfa2447ea88286 Mon Sep 17 00:00:00 2001 From: Aman Negi <37607224+AmanNegi@users.noreply.github.com> Date: Mon, 27 Mar 2023 10:18:26 +0530 Subject: [PATCH 07/37] fix: Update app version in `pubspec.yaml` (#2104) * fix: Update app version in `pubspec.yaml` * fix: Remove Build Number - Removed unnecessary build number mentioned in the `pubspec.yaml` --- .../presentation/widgets/float_bubble/question_bubble.dart | 3 +-- frontend/appflowy_flutter/pubspec.yaml | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index bce35021ef..c6bb7f3ca0 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -135,7 +135,6 @@ class FlowyVersionDescription extends CustomActionCell { PackageInfo packageInfo = snapshot.data; String appName = packageInfo.appName; String version = packageInfo.version; - String buildNumber = packageInfo.buildNumber; return SizedBox( height: 30, @@ -149,7 +148,7 @@ class FlowyVersionDescription extends CustomActionCell { thickness: 1.0), const VSpace(6), FlowyText( - "$appName $version.$buildNumber", + "$appName $version", color: Theme.of(context).hintColor, ), ], diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index b79bcc5fbe..b9d5b4a875 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 0.1.1 environment: sdk: ">=2.18.0 <3.0.0" @@ -89,7 +89,7 @@ dependencies: google_fonts: ^3.0.1 file_picker: <=5.0.0 percent_indicator: ^4.0.1 - + appflowy_editor_plugins: path: packages/appflowy_editor_plugins calendar_view: ^1.0.1 From 03cd9a69936ad647ade84da2e5ef0d470baccfd9 Mon Sep 17 00:00:00 2001 From: qinluhe <108015703+qinluhe@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:55:24 +0800 Subject: [PATCH 08/37] Refactor tauri document (#2117) * fix: Optimize the re-render node when the selection changes * feat: the feature of delete block * feat: add left tool when hover on block * refactor: document data and update * refactor: document component * refactor: document controller --- frontend/appflowy_tauri/package.json | 8 +- frontend/appflowy_tauri/pnpm-lock.yaml | 85 +- .../block_editor/blocks/text_block/index.ts | 71 -- .../blocks/text_block/text_selection.ts | 35 - .../appflowy_app/block_editor/core/block.ts | 107 --- .../block_editor/core/block_chain.ts | 225 ----- .../block_editor/core/op_adapter.ts | 16 - .../block_editor/core/operation.ts | 153 ---- .../appflowy_app/block_editor/core/sync.ts | 48 -- .../src/appflowy_app/block_editor/index.ts | 60 -- .../block_editor/view/block_position.ts | 73 -- .../appflowy_app/block_editor/view/tree.ts | 165 ---- .../block_editor/view/tree_node.ts | 59 -- .../components/HoveringToolbar/Portal.tsx | 9 - .../BlockComponent/BlockComponet.hooks.ts | 36 - .../components/block/BlockComponent/index.tsx | 91 -- .../block/BlockList/BlockList.hooks.tsx | 92 -- .../block/BlockList/BlockListTitle.tsx | 18 - .../block/BlockList/ListFallbackComponent.tsx | 31 - .../components/block/BlockList/index.tsx | 58 -- .../components/block/BlockSelection/index.tsx | 18 - .../components/block/CodeBlock/index.tsx | 6 - .../components/block/HeadingBlock/index.tsx | 17 - .../block/ListBlock/ColumnListBlock.tsx | 18 - .../block/ListBlock/NumberedListBlock.tsx | 31 - .../components/block/PageBlock/index.tsx | 6 - .../components/block/TextBlock/index.hooks.ts | 98 --- .../components/block/TextBlock/index.tsx | 43 - .../components/document/BlockPortal/index.tsx | 9 + .../BlockSelection/BlockSelection.hooks.tsx | 53 +- .../document/BlockSelection/index.tsx | 23 + .../BlockSideTools/BlockSideTools.hooks.tsx | 126 +++ .../document/BlockSideTools/index.tsx | 36 + .../components/document/CodeBlock/index.tsx | 3 + .../{block => document}/ColumnBlock/index.tsx | 21 +- .../DocumentTitle/DocumentTitle.hooks.ts | 8 + .../document/DocumentTitle/index.tsx | 13 + .../document/HeadingBlock/index.tsx | 17 + .../HoveringToolbar/FormatButton.tsx | 0 .../HoveringToolbar/FormatIcon.tsx | 0 .../HoveringToolbar/index.hooks.ts | 8 +- .../{ => document}/HoveringToolbar/index.tsx | 9 +- .../ListBlock/BulletedListBlock.tsx | 21 +- .../document/ListBlock/ColumnListBlock.tsx | 23 + .../document/ListBlock/NumberedListBlock.tsx | 30 + .../{block => document}/ListBlock/index.tsx | 18 +- .../components/document/Node/Node.hooks.ts | 36 + .../components/document/Node/index.tsx | 42 + .../components/document/Overlay/index.tsx | 13 + .../components/document/Root/Root.hooks.tsx | 16 + .../components/document/Root/Tree.hooks.tsx | 23 + .../components/document/Root/index.tsx | 32 + .../document/TextBlock/BindYjs.hooks.ts | 61 ++ .../{block => document}/TextBlock/Leaf.tsx | 0 .../document/TextBlock/TextBlock.hooks.ts | 110 +++ .../components/document/TextBlock/index.tsx | 46 + .../VirtualizerList/VirtualizerList.hooks.tsx | 21 + .../document/VirtualizerList/index.tsx | 59 ++ .../ErrorBoundaryFallbackComponent.tsx | 12 + .../document/_shared/SubscribeNode.hooks.ts | 32 + .../src/appflowy_app/constants/toolbar.ts | 14 - .../src/appflowy_app/interfaces/document.ts | 31 + .../src/appflowy_app/interfaces/index.ts | 113 +-- .../effects/document/document_controller.ts | 50 ++ .../reducers/document}/region_grid.ts | 17 +- .../stores/reducers/document/slice.ts | 132 +++ .../src/appflowy_app/stores/store.ts | 2 + .../src/appflowy_app/utils/block.ts | 25 - .../src/appflowy_app/utils/block_selection.ts | 36 - .../src/appflowy_app/utils/slate/context.ts | 6 - .../src/appflowy_app/utils/tool.ts | 52 ++ .../appflowy_app/views/DocumentPage.hooks.ts | 792 +----------------- .../src/appflowy_app/views/DocumentPage.tsx | 22 +- 73 files changed, 1249 insertions(+), 2641 deletions(-) delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/text_selection.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block_chain.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/core/op_adapter.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/core/operation.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/core/sync.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/view/block_position.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree_node.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/Portal.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/BlockComponet.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/index.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockList.hooks.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockListTitle.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/ListFallbackComponent.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/index.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/index.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/CodeBlock/index.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/HeadingBlock/index.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/ColumnListBlock.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/NumberedListBlock.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/PageBlock/index.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/BlockPortal/index.tsx rename frontend/appflowy_tauri/src/appflowy_app/components/{block => document}/BlockSelection/BlockSelection.hooks.tsx (69%) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/BlockSideTools.hooks.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/index.tsx rename frontend/appflowy_tauri/src/appflowy_app/components/{block => document}/ColumnBlock/index.tsx (59%) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/DocumentTitle.hooks.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/HeadingBlock/index.tsx rename frontend/appflowy_tauri/src/appflowy_app/components/{ => document}/HoveringToolbar/FormatButton.tsx (100%) rename frontend/appflowy_tauri/src/appflowy_app/components/{ => document}/HoveringToolbar/FormatIcon.tsx (100%) rename frontend/appflowy_tauri/src/appflowy_app/components/{ => document}/HoveringToolbar/index.hooks.ts (76%) rename frontend/appflowy_tauri/src/appflowy_app/components/{ => document}/HoveringToolbar/index.tsx (74%) rename frontend/appflowy_tauri/src/appflowy_app/components/{block => document}/ListBlock/BulletedListBlock.tsx (54%) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/ColumnListBlock.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/NumberedListBlock.tsx rename frontend/appflowy_tauri/src/appflowy_app/components/{block => document}/ListBlock/index.tsx (51%) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Node/Node.hooks.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Node/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Overlay/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Root.hooks.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Tree.hooks.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Root/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/BindYjs.hooks.ts rename frontend/appflowy_tauri/src/appflowy_app/components/{block => document}/TextBlock/Leaf.tsx (100%) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/TextBlock.hooks.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/VirtualizerList.hooks.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/ErrorBoundaryFallbackComponent.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/document_controller.ts rename frontend/appflowy_tauri/src/appflowy_app/{block_editor/view => stores/reducers/document}/region_grid.ts (79%) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/block.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/block_selection.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/slate/context.ts diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index 8215381041..9cac4d87c2 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -20,6 +20,7 @@ "@mui/icons-material": "^5.11.11", "@mui/material": "^5.11.12", "@reduxjs/toolkit": "^1.9.2", + "@slate-yjs/core": "^0.3.1", "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.2.0", "events": "^3.3.0", @@ -42,8 +43,9 @@ "slate": "^0.91.4", "slate-react": "^0.91.9", "ts-results": "^3.3.0", - "ulid": "^2.3.0", - "utf8": "^3.0.0" + "utf8": "^3.0.0", + "yjs": "^13.5.51", + "y-indexeddb": "^9.0.9" }, "devDependencies": { "@tauri-apps/cli": "^1.2.2", @@ -53,6 +55,7 @@ "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", "@types/utf8": "^3.0.1", + "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.51.0", "@typescript-eslint/parser": "^5.51.0", "@vitejs/plugin-react": "^3.0.0", @@ -64,6 +67,7 @@ "prettier-plugin-tailwindcss": "^0.2.2", "tailwindcss": "^3.2.7", "typescript": "^4.6.4", + "uuid": "^9.0.0", "vite": "^4.0.0" } } diff --git a/frontend/appflowy_tauri/pnpm-lock.yaml b/frontend/appflowy_tauri/pnpm-lock.yaml index 4402ceca71..426fb22859 100644 --- a/frontend/appflowy_tauri/pnpm-lock.yaml +++ b/frontend/appflowy_tauri/pnpm-lock.yaml @@ -6,6 +6,7 @@ specifiers: '@mui/icons-material': ^5.11.11 '@mui/material': ^5.11.12 '@reduxjs/toolkit': ^1.9.2 + '@slate-yjs/core': ^0.3.1 '@tanstack/react-virtual': 3.0.0-beta.54 '@tauri-apps/api': ^1.2.0 '@tauri-apps/cli': ^1.2.2 @@ -15,6 +16,7 @@ specifiers: '@types/react': ^18.0.15 '@types/react-dom': ^18.0.6 '@types/utf8': ^3.0.1 + '@types/uuid': ^9.0.1 '@typescript-eslint/eslint-plugin': ^5.51.0 '@typescript-eslint/parser': ^5.51.0 '@vitejs/plugin-react': ^3.0.0 @@ -31,6 +33,7 @@ specifiers: postcss: ^8.4.21 prettier: 2.8.4 prettier-plugin-tailwindcss: ^0.2.2 + protoc-gen-ts: ^0.8.5 react: ^18.2.0 react-dom: ^18.2.0 react-error-boundary: ^3.1.4 @@ -45,9 +48,11 @@ specifiers: tailwindcss: ^3.2.7 ts-results: ^3.3.0 typescript: ^4.6.4 - ulid: ^2.3.0 utf8: ^3.0.0 + uuid: ^9.0.0 vite: ^4.0.0 + y-indexeddb: ^9.0.9 + yjs: ^13.5.51 dependencies: '@emotion/react': 11.10.6_pmekkgnqduwlme35zpnqhenc34 @@ -55,6 +60,7 @@ dependencies: '@mui/icons-material': 5.11.11_ao76n7r2cajsoyr3cbwrn7geoi '@mui/material': 5.11.12_xqeqsl5kvjjtyxwyi3jhw3yuli '@reduxjs/toolkit': 1.9.3_k4ae6lp43ej6mezo3ztvx6pykq + '@slate-yjs/core': 0.3.1_slate@0.91.4+yjs@13.5.51 '@tanstack/react-virtual': 3.0.0-beta.54_react@18.2.0 '@tauri-apps/api': 1.2.0 events: 3.3.0 @@ -64,6 +70,7 @@ dependencies: is-hotkey: 0.2.0 jest: 29.5.0_@types+node@18.14.6 nanoid: 4.0.1 + protoc-gen-ts: 0.8.6_ss7alqtodw6rv4lluxhr36xjoa react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-error-boundary: 3.1.4_react@18.2.0 @@ -76,8 +83,9 @@ dependencies: slate: 0.91.4 slate-react: 0.91.9_6tgy34rvmll7duwkm4ydcekf3u ts-results: 3.3.0 - ulid: 2.3.0 utf8: 3.0.0 + y-indexeddb: 9.0.9_yjs@13.5.51 + yjs: 13.5.51 devDependencies: '@tauri-apps/cli': 1.2.3 @@ -87,6 +95,7 @@ devDependencies: '@types/react': 18.0.28 '@types/react-dom': 18.0.11 '@types/utf8': 3.0.1 + '@types/uuid': 9.0.1 '@typescript-eslint/eslint-plugin': 5.54.0_6mj2wypvdnknez7kws2nfdgupi '@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu '@vitejs/plugin-react': 3.1.0_vite@4.1.4 @@ -98,6 +107,7 @@ devDependencies: prettier-plugin-tailwindcss: 0.2.4_prettier@2.8.4 tailwindcss: 3.2.7_postcss@8.4.21 typescript: 4.9.5 + uuid: 9.0.0 vite: 4.1.4_@types+node@18.14.6 packages: @@ -1308,6 +1318,17 @@ packages: '@sinonjs/commons': 2.0.0 dev: false + /@slate-yjs/core/0.3.1_slate@0.91.4+yjs@13.5.51: + resolution: {integrity: sha512-8nvS9m5FhMNONgydAfzwDCUhuoWbgzx5Bvw1/foSe+JO331UOT1xAKbUX5FzGCOunUcbRjMPXSdNyiPc0dodJg==} + peerDependencies: + slate: '>=0.70.0' + yjs: ^13.5.29 + dependencies: + slate: 0.91.4 + y-protocols: 1.0.5 + yjs: 13.5.51 + dev: false + /@tanstack/react-virtual/3.0.0-beta.54_react@18.2.0: resolution: {integrity: sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ==} peerDependencies: @@ -1553,6 +1574,10 @@ packages: resolution: {integrity: sha512-1EkWuw7rT3BMz2HpmcEOr/HL61mWNA6Ulr/KdbXR9AI0A55wD4Qfv8hizd8Q1DnknSIzzDvQmvvY/guvX7jjZA==} dev: true + /@types/uuid/9.0.1: + resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} + dev: true + /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: false @@ -3050,6 +3075,10 @@ packages: /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + /isomorphic.js/0.2.5: + resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} + dev: false + /istanbul-lib-coverage/3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} @@ -3575,6 +3604,14 @@ packages: type-check: 0.4.0 dev: true + /lib0/0.2.73: + resolution: {integrity: sha512-aJJIElCLWnHMcYZPtsM07QoSfHwpxCy4VUzBYGXFYEmh/h2QS5uZNbCCfL0CqnkOE30b7Tp9DVfjXag+3qzZjQ==} + engines: {node: '>=14'} + hasBin: true + dependencies: + isomorphic.js: 0.2.5 + dev: false + /lilconfig/2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -4055,6 +4092,17 @@ packages: object-assign: 4.1.1 react-is: 16.13.1 + /protoc-gen-ts/0.8.6_ss7alqtodw6rv4lluxhr36xjoa: + resolution: {integrity: sha512-66oeorGy4QBvYjQGd/gaeOYyFqKyRmRgTpofmnw8buMG0P7A0jQjoKSvKJz5h5tNUaVkIzvGBUTRVGakrhhwpA==} + hasBin: true + peerDependencies: + google-protobuf: ^3.13.0 + typescript: 4.x.x + dependencies: + google-protobuf: 3.21.2 + typescript: 4.9.5 + dev: false + /punycode/2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -4678,12 +4726,6 @@ packages: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} hasBin: true - dev: true - - /ulid/2.3.0: - resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==} - hasBin: true - dev: false /unbox-primitive/1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -4726,6 +4768,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid/9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: true + /v8-to-istanbul/9.1.0: resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} engines: {node: '>=10.12.0'} @@ -4839,6 +4886,21 @@ packages: engines: {node: '>=0.4'} dev: true + /y-indexeddb/9.0.9_yjs@13.5.51: + resolution: {integrity: sha512-GcJbiJa2eD5hankj46Hea9z4hbDnDjvh1fT62E5SpZRsv8GcEemw34l1hwI2eknGcv5Ih9JfusT37JLx9q3LFg==} + peerDependencies: + yjs: ^13.0.0 + dependencies: + lib0: 0.2.73 + yjs: 13.5.51 + dev: false + + /y-protocols/1.0.5: + resolution: {integrity: sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==} + dependencies: + lib0: 0.2.73 + dev: false + /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4872,6 +4934,13 @@ packages: yargs-parser: 21.1.1 dev: false + /yjs/13.5.51: + resolution: {integrity: sha512-F1Nb3z3TdandD80IAeQqgqy/2n9AhDLcXoBhZvCUX1dNVe0ef7fIwi6MjSYaGAYF2Ev8VcLcsGnmuGGOl7AWbw==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + dependencies: + lib0: 0.2.73 + dev: false + /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/index.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/index.ts deleted file mode 100644 index de42c3c373..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { BaseEditor, BaseSelection, Descendant } from "slate"; -import { TreeNode } from '$app/block_editor/view/tree_node'; -import { Operation } from "$app/block_editor/core/operation"; -import { TextBlockSelectionManager } from './text_selection'; - -export class TextBlockManager { - public selectionManager: TextBlockSelectionManager; - constructor(private operation: Operation) { - this.selectionManager = new TextBlockSelectionManager(); - } - - setSelection(node: TreeNode, selection: BaseSelection) { - // console.log(node.id, selection); - this.selectionManager.setSelection(node.id, selection) - } - - update(node: TreeNode, path: string[], data: Descendant[]) { - this.operation.updateNode(node.id, path, data); - } - - splitNode(node: TreeNode, editor: BaseEditor) { - const focus = editor.selection?.focus; - const path = focus?.path || [0, editor.children.length - 1]; - const offset = focus?.offset || 0; - const parentIndex = path[0]; - const index = path[1]; - const editorNode = editor.children[parentIndex]; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const children: { [key: string]: boolean | string; text: string }[] = editorNode.children; - const retainItems = children.filter((_: any, i: number) => i < index); - const splitItem: { [key: string]: boolean | string } = children[index]; - const text = splitItem.text.toString(); - const prevText = text.substring(0, offset); - const afterText = text.substring(offset); - retainItems.push({ - ...splitItem, - text: prevText - }); - - const removeItems = children.filter((_: any, i: number) => i > index); - - const data = { - type: node.type, - data: { - ...node.data, - content: [ - { - ...splitItem, - text: afterText - }, - ...removeItems - ] - } - }; - - const newBlock = this.operation.splitNode(node.id, { - path: ['data', 'content'], - value: retainItems, - }, data); - newBlock && this.selectionManager.focusStart(newBlock.id); - } - - destroy() { - this.selectionManager.destroy(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this.operation = null; - } - -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/text_selection.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/text_selection.ts deleted file mode 100644 index b25d7f6268..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/text_selection.ts +++ /dev/null @@ -1,35 +0,0 @@ -export class TextBlockSelectionManager { - private focusId = ''; - private selection?: any; - - getFocusSelection() { - return { - focusId: this.focusId, - selection: this.selection - } - } - - focusStart(blockId: string) { - this.focusId = blockId; - this.setSelection(blockId, { - focus: { - path: [0, 0], - offset: 0, - }, - anchor: { - path: [0, 0], - offset: 0, - }, - }) - } - - setSelection(blockId: string, selection: any) { - this.focusId = blockId; - this.selection = selection; - } - - destroy() { - this.focusId = ''; - this.selection = undefined; - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block.ts deleted file mode 100644 index c550213daa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { BlockType, BlockData } from '$app/interfaces/index'; -import { generateBlockId } from '$app/utils/block'; - -/** - * Represents a single block of content in a document. - */ -export class Block { - id: string; - type: T; - data: BlockData; - parent: Block | null = null; // Pointer to the parent block - prev: Block | null = null; // Pointer to the previous sibling block - next: Block | null = null; // Pointer to the next sibling block - firstChild: Block | null = null; // Pointer to the first child block - - constructor(id: string, type: T, data: BlockData) { - this.id = id; - this.type = type; - this.data = data; - } - - /** - * Adds a new child block to the beginning of the current block's children list. - * - * @param {Object} content - The content of the new block, including its type and data. - * @param {string} content.type - The type of the new block. - * @param {Object} content.data - The data associated with the new block. - * @returns {Block} The newly created child block. - */ - prependChild(content: { type: T, data: BlockData }): Block | null { - const id = generateBlockId(); - const newBlock = new Block(id, content.type, content.data); - newBlock.reposition(this, null); - return newBlock; - } - - /** - * Add a new sibling block after this block. - * - * @param content The type and data for the new sibling block. - * @returns The newly created sibling block. - */ - addSibling(content: { type: T, data: BlockData }): Block | null { - const id = generateBlockId(); - const newBlock = new Block(id, content.type, content.data); - newBlock.reposition(this.parent, this); - return newBlock; - } - - /** - * Remove this block and its descendants from the tree. - * - */ - remove() { - this.detach(); - let child = this.firstChild; - while (child) { - const next = child.next; - child.remove(); - child = next; - } - } - - reposition(newParent: Block | null, newPrev: Block | null) { - // Update the block's parent and siblings - this.parent = newParent; - this.prev = newPrev; - this.next = null; - - if (newParent) { - const prev = newPrev; - if (!prev) { - const next = newParent.firstChild; - newParent.firstChild = this; - if (next) { - this.next = next; - next.prev = this; - } - - } else { - // Update the next and prev pointers of the newPrev and next blocks - if (prev.next !== this) { - const next = prev.next; - if (next) { - next.prev = this - this.next = next; - } - prev.next = this; - } - } - - } - } - - // detach the block from its current position in the tree - detach() { - if (this.prev) { - this.prev.next = this.next; - } else if (this.parent) { - this.parent.firstChild = this.next; - } - if (this.next) { - this.next.prev = this.prev; - } - } - -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block_chain.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block_chain.ts deleted file mode 100644 index 877f3592df..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block_chain.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { BlockData, BlockInterface, BlockType } from '$app/interfaces/index'; -import { set } from '../../utils/tool'; -import { Block } from './block'; -export interface BlockChangeProps { - block?: Block, - startBlock?: Block, - endBlock?: Block, - oldParentId?: string, - oldPrevId?: string -} -export class BlockChain { - private map: Map> = new Map(); - public head: Block | null = null; - - constructor(private onBlockChange: (command: string, data: BlockChangeProps) => void) { - - } - /** - * generate blocks from doc data - * @param id doc id - * @param map doc data - */ - rebuild = (id: string, map: Record>) => { - this.map.clear(); - this.head = this.createBlock(id, map[id].type, map[id].data); - - const callback = (block: Block) => { - const firstChildId = map[block.id].firstChild; - const nextId = map[block.id].next; - if (!block.firstChild && firstChildId) { - block.firstChild = this.createBlock(firstChildId, map[firstChildId].type, map[firstChildId].data); - block.firstChild.parent = block; - block.firstChild.prev = null; - } - if (!block.next && nextId) { - block.next = this.createBlock(nextId, map[nextId].type, map[nextId].data); - block.next.parent = block.parent; - block.next.prev = block; - } - } - this.traverse(callback); - } - - /** - * Traversing the block list from front to back - * @param callback It will be call when the block visited - * @param block block item, it will be equal head node when the block item is undefined - */ - traverse(callback: (_block: Block) => void, block?: Block) { - let currentBlock: Block | null = block || this.head; - while (currentBlock) { - callback(currentBlock); - if (currentBlock.firstChild) { - this.traverse(callback, currentBlock.firstChild); - } - currentBlock = currentBlock.next; - } - } - - /** - * get block data - * @param blockId string - * @returns Block - */ - getBlock = (blockId: string) => { - return this.map.get(blockId) || null; - } - - destroy() { - this.map.clear(); - this.head = null; - this.onBlockChange = () => null; - } - - /** - * Adds a new child block to the beginning of the current block's children list. - * - * @param {string} parentId - * @param {Object} content - The content of the new block, including its type and data. - * @param {string} content.type - The type of the new block. - * @param {Object} content.data - The data associated with the new block. - * @returns {Block} The newly created child block. - */ - prependChild(blockId: string, content: { type: BlockType, data: BlockData }): Block | null { - const parent = this.getBlock(blockId); - if (!parent) return null; - const newBlock = parent.prependChild(content); - - if (newBlock) { - this.map.set(newBlock?.id, newBlock); - this.onBlockChange('insert', { block: newBlock }); - } - - return newBlock; - } - - /** - * Add a new sibling block after this block. - * @param {string} blockId - * @param content The type and data for the new sibling block. - * @returns The newly created sibling block. - */ - addSibling(blockId: string, content: { type: BlockType, data: BlockData }): Block | null { - const block = this.getBlock(blockId); - if (!block) return null; - const newBlock = block.addSibling(content); - if (newBlock) { - this.map.set(newBlock?.id, newBlock); - this.onBlockChange('insert', { block: newBlock }); - } - return newBlock; - } - - /** - * Remove this block and its descendants from the tree. - * @param {string} blockId - */ - remove(blockId: string) { - const block = this.getBlock(blockId); - if (!block) return; - block.remove(); - this.map.delete(block.id); - this.onBlockChange('delete', { block }); - return block; - } - - /** - * Move this block to a new position in the tree. - * @param {string} blockId - * @param newParentId The new parent block of this block. If null, the block becomes a top-level block. - * @param newPrevId The new previous sibling block of this block. If null, the block becomes the first child of the new parent. - * @returns This block after it has been moved. - */ - move(blockId: string, newParentId: string, newPrevId: string): Block | null { - const block = this.getBlock(blockId); - if (!block) return null; - const oldParentId = block.parent?.id; - const oldPrevId = block.prev?.id; - block.detach(); - const newParent = this.getBlock(newParentId); - const newPrev = this.getBlock(newPrevId); - block.reposition(newParent, newPrev); - this.onBlockChange('move', { - block, - oldParentId, - oldPrevId - }); - return block; - } - - updateBlock(id: string, data: { path: string[], value: any }) { - const block = this.getBlock(id); - if (!block) return null; - - set(block, data.path, data.value); - this.onBlockChange('update', { - block - }); - return block; - } - - - moveBulk(startBlockId: string, endBlockId: string, newParentId: string, newPrevId: string): [Block, Block] | null { - const startBlock = this.getBlock(startBlockId); - const endBlock = this.getBlock(endBlockId); - if (!startBlock || !endBlock) return null; - - if (startBlockId === endBlockId) { - const block = this.move(startBlockId, newParentId, ''); - if (!block) return null; - return [block, block]; - } - - const oldParent = startBlock.parent; - const prev = startBlock.prev; - const newParent = this.getBlock(newParentId); - if (!oldParent || !newParent) return null; - - if (oldParent.firstChild === startBlock) { - oldParent.firstChild = endBlock.next; - } else if (prev) { - prev.next = endBlock.next; - } - startBlock.prev = null; - endBlock.next = null; - - startBlock.parent = newParent; - endBlock.parent = newParent; - const newPrev = this.getBlock(newPrevId); - if (!newPrev) { - const firstChild = newParent.firstChild; - newParent.firstChild = startBlock; - if (firstChild) { - endBlock.next = firstChild; - firstChild.prev = endBlock; - } - } else { - const next = newPrev.next; - newPrev.next = startBlock; - endBlock.next = next; - if (next) { - next.prev = endBlock; - } - } - - this.onBlockChange('move', { - startBlock, - endBlock, - oldParentId: oldParent.id, - oldPrevId: prev?.id - }); - - return [ - startBlock, - endBlock - ]; - } - - - private createBlock(id: string, type: BlockType, data: BlockData) { - const block = new Block(id, type, data); - this.map.set(id, block); - return block; - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/op_adapter.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/op_adapter.ts deleted file mode 100644 index 0c5c0b3190..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/op_adapter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BackendOp, LocalOp } from "$app/interfaces"; - -export class OpAdapter { - - toBackendOp(localOp: LocalOp): BackendOp { - const backendOp: BackendOp = { ...localOp }; - // switch localOp type and generate backendOp - return backendOp; - } - - toLocalOp(backendOp: BackendOp): LocalOp { - const localOp: LocalOp = { ...backendOp }; - // switch backendOp type and generate localOp - return localOp; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/operation.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/operation.ts deleted file mode 100644 index 38f3a3fb76..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/operation.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { BlockChain } from './block_chain'; -import { BlockInterface, BlockType, InsertOpData, LocalOp, UpdateOpData, moveOpData, moveRangeOpData, removeOpData, BlockData } from '$app/interfaces'; -import { BlockEditorSync } from './sync'; -import { Block } from './block'; - -export class Operation { - private sync: BlockEditorSync; - constructor(private blockChain: BlockChain) { - this.sync = new BlockEditorSync(); - } - - - splitNode( - retainId: string, - retainData: { path: string[], value: any }, - newBlockData: { - type: BlockType; - data: BlockData - }) { - const ops: { - type: LocalOp['type']; - data: LocalOp['data']; - }[] = []; - const newBlock = this.blockChain.addSibling(retainId, newBlockData); - const parentId = newBlock?.parent?.id; - const retainBlock = this.blockChain.getBlock(retainId); - if (!newBlock || !parentId || !retainBlock) return null; - - const insertOp = this.getInsertNodeOp({ - id: newBlock.id, - next: newBlock.next?.id || null, - firstChild: newBlock.firstChild?.id || null, - data: newBlock.data, - type: newBlock.type, - }, parentId, retainId); - - const updateOp = this.getUpdateNodeOp(retainId, retainData.path, retainData.value); - this.blockChain.updateBlock(retainId, retainData); - - ops.push(insertOp, updateOp); - const startBlock = retainBlock.firstChild; - if (startBlock) { - const startBlockId = startBlock.id; - let next: Block | null = startBlock.next; - let endBlockId = startBlockId; - while (next) { - endBlockId = next.id; - next = next.next; - } - - const moveOp = this.getMoveRangeOp([startBlockId, endBlockId], newBlock.id); - this.blockChain.moveBulk(startBlockId, endBlockId, newBlock.id, ''); - ops.push(moveOp); - } - - this.sync.sendOps(ops); - - return newBlock; - } - - updateNode(blockId: string, path: string[], value: T) { - const op = this.getUpdateNodeOp(blockId, path, value); - this.blockChain.updateBlock(blockId, { - path, - value - }); - this.sync.sendOps([op]); - } - private getUpdateNodeOp(blockId: string, path: string[], value: T): { - type: 'update', - data: UpdateOpData - } { - return { - type: 'update', - data: { - blockId, - path: path, - value - } - }; - } - - private getInsertNodeOp(block: T, parentId: string, prevId?: string): { - type: 'insert'; - data: InsertOpData - } { - return { - type: 'insert', - data: { - block, - parentId, - prevId - } - } - } - - private getMoveRangeOp(range: [string, string], newParentId: string, newPrevId?: string): { - type: 'move_range', - data: moveRangeOpData - } { - return { - type: 'move_range', - data: { - range, - newParentId, - newPrevId, - } - } - } - - private getMoveOp(blockId: string, newParentId: string, newPrevId?: string): { - type: 'move', - data: moveOpData - } { - return { - type: 'move', - data: { - blockId, - newParentId, - newPrevId - } - } - } - - private getRemoveOp(blockId: string): { - type: 'remove' - data: removeOpData - } { - return { - type: 'remove', - data: { - blockId - } - } - } - - applyOperation(op: LocalOp) { - switch (op.type) { - case 'insert': - - break; - - default: - break; - } - } - - destroy() { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this.blockChain = null; - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/sync.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/sync.ts deleted file mode 100644 index 24070c0cd5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/core/sync.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BackendOp, LocalOp } from '$app/interfaces'; -import { OpAdapter } from './op_adapter'; - -/** - * BlockEditorSync is a class that synchronizes changes made to a block chain with a server. - * It allows for adding, removing, and moving blocks in the chain, and sends pending operations to the server. - */ -export class BlockEditorSync { - private version = 0; - private opAdapter: OpAdapter; - private pendingOps: BackendOp[] = []; - private appliedOps: LocalOp[] = []; - - constructor() { - this.opAdapter = new OpAdapter(); - } - - private applyOp(op: BackendOp): void { - const localOp = this.opAdapter.toLocalOp(op); - this.appliedOps.push(localOp); - } - - private receiveOps(ops: BackendOp[]): void { - // Apply the incoming operations to the local document - ops.sort((a, b) => a.version - b.version); - for (const op of ops) { - this.applyOp(op); - } - } - - private resolveConflict(): void { - // Implement conflict resolution logic here - } - - public sendOps(ops: { - type: LocalOp["type"]; - data: LocalOp["data"] - }[]) { - const backendOps = ops.map(op => this.opAdapter.toBackendOp({ - ...op, - version: this.version - })); - this.pendingOps.push(...backendOps); - // Send the pending operations to the server - console.log('==== sync pending ops ====', [...this.pendingOps]); - } - -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/index.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/index.ts deleted file mode 100644 index 658b284906..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Import dependencies -import { BlockInterface } from '../interfaces'; -import { BlockChain, BlockChangeProps } from './core/block_chain'; -import { RenderTree } from './view/tree'; -import { Operation } from './core/operation'; - -/** - * The BlockEditor class manages a block chain and a render tree for a document editor. - * The block chain stores the content blocks of the document in sequence, while the - * render tree displays the document as a hierarchical tree structure. - */ -export class BlockEditor { - // Public properties - public blockChain: BlockChain; // (local data) the block chain used to store the document - public renderTree: RenderTree; // the render tree used to display the document - public operation: Operation; - /** - * Constructs a new BlockEditor object. - * @param id - the ID of the document - * @param data - the initial data for the document - */ - constructor(private id: string, data: Record) { - // Create the block chain and render tree - this.blockChain = new BlockChain(this.blockChange); - this.operation = new Operation(this.blockChain); - this.changeDoc(id, data); - - this.renderTree = new RenderTree(this.blockChain); - } - - /** - * Updates the document ID and block chain when the document changes. - * @param id - the new ID of the document - * @param data - the updated data for the document - */ - changeDoc = (id: string, data: Record) => { - console.log('==== change document ====', id, data); - - // Update the document ID and rebuild the block chain - this.id = id; - this.blockChain.rebuild(id, data); - } - - - /** - * Destroys the block chain and render tree. - */ - destroy = () => { - // Destroy the block chain and render tree - this.blockChain.destroy(); - this.renderTree.destroy(); - this.operation.destroy(); - } - - private blockChange = (command: string, data: BlockChangeProps) => { - this.renderTree.onBlockChange(command, data); - } - -} - diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/block_position.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/block_position.ts deleted file mode 100644 index a2841d8a3b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/block_position.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { RegionGrid, BlockPosition } from './region_grid'; -export class BlockPositionManager { - private regionGrid: RegionGrid; - private viewportBlocks: Set = new Set(); - private blockPositions: Map = new Map(); - private observer: IntersectionObserver; - private container: HTMLDivElement | null = null; - - constructor(container: HTMLDivElement) { - this.container = container; - this.regionGrid = new RegionGrid(container.offsetHeight); - this.observer = new IntersectionObserver((entries) => { - for (const entry of entries) { - const blockId = entry.target.getAttribute('data-block-id'); - if (!blockId) return; - if (entry.isIntersecting) { - this.updateBlockPosition(blockId); - this.viewportBlocks.add(blockId); - } else { - this.viewportBlocks.delete(blockId); - } - } - }, { root: container }); - } - - observeBlock(node: HTMLDivElement) { - this.observer.observe(node); - return { - unobserve: () => this.observer.unobserve(node), - } - } - - getBlockPosition(blockId: string) { - if (!this.blockPositions.has(blockId)) { - this.updateBlockPosition(blockId); - } - return this.blockPositions.get(blockId); - } - - updateBlockPosition(blockId: string) { - if (!this.container) return; - const node = document.querySelector(`[data-block-id=${blockId}]`) as HTMLDivElement; - if (!node) return; - const rect = node.getBoundingClientRect(); - const position = { - id: blockId, - x: rect.x, - y: rect.y + this.container.scrollTop, - height: rect.height, - width: rect.width - }; - const prevPosition = this.blockPositions.get(blockId); - if (prevPosition && prevPosition.x === position.x && - prevPosition.y === position.y && - prevPosition.height === position.height && - prevPosition.width === position.width) { - return; - } - this.blockPositions.set(blockId, position); - this.regionGrid.removeBlock(blockId); - this.regionGrid.addBlock(position); - } - - getIntersectBlocks(startX: number, startY: number, endX: number, endY: number): BlockPosition[] { - return this.regionGrid.getIntersectBlocks(startX, startY, endX, endY); - } - - destroy() { - this.container = null; - this.observer.disconnect(); - } - -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree.ts deleted file mode 100644 index 4eb136ff09..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { BlockChain, BlockChangeProps } from '../core/block_chain'; -import { Block } from '../core/block'; -import { TreeNode } from "./tree_node"; -import { BlockPositionManager } from './block_position'; -import { filterSelections } from '@/appflowy_app/utils/block_selection'; - -export class RenderTree { - public blockPositionManager?: BlockPositionManager; - - private map: Map = new Map(); - private root: TreeNode | null = null; - private selections: Set = new Set(); - constructor(private blockChain: BlockChain) { - } - - - createPositionManager(container: HTMLDivElement) { - this.blockPositionManager = new BlockPositionManager(container); - } - - observeBlock(node: HTMLDivElement) { - return this.blockPositionManager?.observeBlock(node); - } - - getBlockPosition(nodeId: string) { - return this.blockPositionManager?.getBlockPosition(nodeId) || null; - } - /** - * Get the TreeNode data by nodeId - * @param nodeId string - * @returns TreeNode|null - */ - getTreeNode = (nodeId: string): TreeNode | null => { - // Return the TreeNode instance from the map or null if it does not exist - return this.map.get(nodeId) || null; - } - - private createNode(block: Block): TreeNode { - if (this.map.has(block.id)) { - return this.map.get(block.id)!; - } - const node = new TreeNode(block); - this.map.set(block.id, node); - return node; - } - - - buildDeep(rootId: string): TreeNode | null { - this.map.clear(); - // Define a callback function for the blockChain.traverse() method - const callback = (block: Block) => { - // Check if the TreeNode instance already exists in the map - const node = this.createNode(block); - - // Add the TreeNode instance to the map - this.map.set(block.id, node); - - // Add the first child of the block as a child of the current TreeNode instance - const firstChild = block.firstChild; - if (firstChild) { - const child = this.createNode(firstChild); - node.addChild(child); - this.map.set(child.id, child); - } - - // Add the next block as a sibling of the current TreeNode instance - const next = block.next; - if (next) { - const nextNode = this.createNode(next); - node.parent?.addChild(nextNode); - this.map.set(next.id, nextNode); - } - } - - // Traverse the blockChain using the callback function - this.blockChain.traverse(callback); - - // Get the root node from the map and return it - const root = this.map.get(rootId)!; - this.root = root; - return root || null; - } - - - forceUpdate(nodeId: string, shouldUpdateChildren = false) { - const block = this.blockChain.getBlock(nodeId); - if (!block) return null; - const node = this.createNode(block); - if (!node) return null; - - if (shouldUpdateChildren) { - const children: TreeNode[] = []; - let childBlock = block.firstChild; - - while(childBlock) { - const child = this.createNode(childBlock); - child.update(childBlock, child.children); - children.push(child); - childBlock = childBlock.next; - } - - node.update(block, children); - node?.reRender(); - node?.children.forEach(child => { - child.reRender(); - }) - } else { - node.update(block, node.children); - node?.reRender(); - } - } - - onBlockChange(command: string, data: BlockChangeProps) { - const { block, startBlock, endBlock, oldParentId = '', oldPrevId = '' } = data; - switch (command) { - case 'insert': - if (block?.parent) this.forceUpdate(block.parent.id, true); - break; - case 'update': - this.forceUpdate(block!.id); - break; - case 'move': - if (oldParentId) this.forceUpdate(oldParentId, true); - if (block?.parent) this.forceUpdate(block.parent.id, true); - if (startBlock?.parent) this.forceUpdate(startBlock.parent.id, true); - break; - default: - break; - } - - } - - updateSelections(selections: string[]) { - const newSelections = filterSelections(selections, this.map); - - let isDiff = false; - if (newSelections.length !== this.selections.size) { - isDiff = true; - } - - const selectedBlocksSet = new Set(newSelections); - if (Array.from(this.selections).some((id) => !selectedBlocksSet.has(id))) { - isDiff = true; - } - - if (isDiff) { - const shouldUpdateIds = new Set([...this.selections, ...newSelections]); - this.selections = selectedBlocksSet; - shouldUpdateIds.forEach((id) => this.forceUpdate(id)); - } - } - - isSelected(nodeId: string) { - return this.selections.has(nodeId); - } - - /** - * Destroy the RenderTreeRectManager instance - */ - destroy() { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this.blockChain = null; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree_node.ts b/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree_node.ts deleted file mode 100644 index 9ed78bd4b4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree_node.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { BlockData, BlockType } from '$app/interfaces/index'; -import { Block } from '../core/block'; - -/** - * Represents a node in a tree structure of blocks. - */ -export class TreeNode { - id: string; - type: BlockType; - parent: TreeNode | null = null; - children: TreeNode[] = []; - data: BlockData; - - private forceUpdate?: () => void; - - /** - * Create a new TreeNode instance. - * @param block - The block data used to create the node. - */ - constructor(private _block: Block) { - this.id = _block.id; - this.data = _block.data; - this.type = _block.type; - } - - registerUpdate(forceUpdate: () => void) { - this.forceUpdate = forceUpdate; - } - - unregisterUpdate() { - this.forceUpdate = undefined; - } - - reRender() { - this.forceUpdate?.(); - } - - update(block: Block, children: TreeNode[]) { - this.data = block.data; - this.children = []; - children.forEach(child => { - this.addChild(child); - }) - } - - /** - * Add a child node to the current node. - * @param node - The child node to add. - */ - addChild(node: TreeNode) { - node.parent = this; - this.children.push(node); - } - - get block() { - return this._block; - } - -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/Portal.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/Portal.tsx deleted file mode 100644 index 0176c8f429..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/Portal.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import ReactDOM from 'react-dom'; - -const Portal = ({ blockId, children }: { blockId: string; children: JSX.Element }) => { - const root = document.querySelectorAll(`[data-block-id=${blockId}] > .block-overlay`)[0]; - - return typeof document === 'object' && root ? ReactDOM.createPortal(children, root) : null; -}; - -export default Portal; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/BlockComponet.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/BlockComponet.hooks.ts deleted file mode 100644 index 20e31a1793..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/BlockComponet.hooks.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect, useState, useRef, useContext } from 'react'; - -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { BlockContext } from '$app/utils/block'; - -export function useBlockComponent({ - node -}: { - node: TreeNode -}) { - const { blockEditor } = useContext(BlockContext); - - const [version, forceUpdate] = useState(0); - const myRef = useRef(null); - - const isSelected = blockEditor?.renderTree.isSelected(node.id); - - useEffect(() => { - if (!myRef.current) { - return; - } - const observe = blockEditor?.renderTree.observeBlock(myRef.current); - node.registerUpdate(() => forceUpdate((prev) => prev + 1)); - - return () => { - node.unregisterUpdate(); - observe?.unobserve(); - }; - }, []); - return { - version, - myRef, - isSelected, - className: `relative my-[1px] px-1` - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/index.tsx deleted file mode 100644 index 9c8ee223dd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockComponent/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, { forwardRef } from 'react'; -import { BlockCommonProps, BlockType } from '$app/interfaces'; -import PageBlock from '../PageBlock'; -import TextBlock from '../TextBlock'; -import HeadingBlock from '../HeadingBlock'; -import ListBlock from '../ListBlock'; -import CodeBlock from '../CodeBlock'; -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { withErrorBoundary } from 'react-error-boundary'; -import { ErrorBoundaryFallbackComponent } from '../BlockList/BlockList.hooks'; -import { useBlockComponent } from './BlockComponet.hooks'; - -const BlockComponent = forwardRef( - ( - { - node, - renderChild, - ...props - }: { node: TreeNode; renderChild?: (_node: TreeNode) => React.ReactNode } & React.DetailedHTMLProps< - React.HTMLAttributes, - HTMLDivElement - >, - ref: React.ForwardedRef - ) => { - const { myRef, className, version, isSelected } = useBlockComponent({ - node, - }); - - const renderComponent = () => { - let BlockComponentClass: (_: BlockCommonProps) => JSX.Element | null; - switch (node.type) { - case BlockType.PageBlock: - BlockComponentClass = PageBlock; - break; - case BlockType.TextBlock: - BlockComponentClass = TextBlock; - break; - case BlockType.HeadingBlock: - BlockComponentClass = HeadingBlock; - break; - case BlockType.ListBlock: - BlockComponentClass = ListBlock; - break; - case BlockType.CodeBlock: - BlockComponentClass = CodeBlock; - break; - default: - break; - } - - const blockProps: BlockCommonProps = { - version, - node, - }; - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (BlockComponentClass) { - return ; - } - return null; - }; - - return ( -
{ - myRef.current = el; - if (typeof ref === 'function') { - ref(el); - } else if (ref) { - ref.current = el; - } - }} - {...props} - data-block-id={node.id} - data-block-selected={isSelected} - className={props.className ? `${props.className} ${className}` : className} - > - {renderComponent()} - {renderChild ? node.children.map(renderChild) : null} -
- {isSelected ?
: null} -
- ); - } -); - -const ComponentWithErrorBoundary = withErrorBoundary(BlockComponent, { - FallbackComponent: ErrorBoundaryFallbackComponent, -}); -export default React.memo(ComponentWithErrorBoundary); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockList.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockList.hooks.tsx deleted file mode 100644 index 0d673a47e8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockList.hooks.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; -import { BlockEditor } from '@/appflowy_app/block_editor'; -import { TreeNode } from '$app/block_editor/view/tree_node'; -import { Alert } from '@mui/material'; -import { FallbackProps } from 'react-error-boundary'; -import { TextBlockManager } from '@/appflowy_app/block_editor/blocks/text_block'; -import { TextBlockContext } from '@/appflowy_app/utils/slate/context'; -import { useVirtualizer } from '@tanstack/react-virtual'; -export interface BlockListProps { - blockId: string; - blockEditor: BlockEditor; -} - -const defaultSize = 45; - -export function useBlockList({ blockId, blockEditor }: BlockListProps) { - const [root, setRoot] = useState(null); - - const parentRef = useRef(null); - - const rowVirtualizer = useVirtualizer({ - count: root?.children.length || 0, - getScrollElement: () => parentRef.current, - overscan: 5, - estimateSize: () => { - return defaultSize; - }, - }); - - const [version, forceUpdate] = useState(0); - - const buildDeepTree = useCallback(() => { - const treeNode = blockEditor.renderTree.buildDeep(blockId); - setRoot(treeNode); - }, [blockEditor]); - - useEffect(() => { - if (!parentRef.current) return; - blockEditor.renderTree.createPositionManager(parentRef.current); - buildDeepTree(); - - return () => { - blockEditor.destroy(); - }; - }, [blockId, blockEditor]); - - useEffect(() => { - root?.registerUpdate(() => forceUpdate((prev) => prev + 1)); - return () => { - root?.unregisterUpdate(); - }; - }, [root]); - - return { - root, - rowVirtualizer, - parentRef, - blockEditor, - }; -} - -export function ErrorBoundaryFallbackComponent({ error, resetErrorBoundary }: FallbackProps) { - return ( - -

Something went wrong:

-
{error.message}
- -
- ); -} - -export function withTextBlockManager(Component: (props: BlockListProps) => React.ReactElement) { - return (props: BlockListProps) => { - const textBlockManager = new TextBlockManager(props.blockEditor.operation); - - useEffect(() => { - return () => { - textBlockManager.destroy(); - }; - }, []); - - return ( - - - - ); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockListTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockListTitle.tsx deleted file mode 100644 index f74ae72283..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/BlockListTitle.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import TextBlock from '../TextBlock'; -import { TreeNode } from '$app/block_editor/view/tree_node'; - -export default function BlockListTitle({ node }: { node: TreeNode | null }) { - if (!node) return null; - return ( -
- -
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/ListFallbackComponent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/ListFallbackComponent.tsx deleted file mode 100644 index 6078180374..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/ListFallbackComponent.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from 'react'; -import Typography, { TypographyProps } from '@mui/material/Typography'; -import Skeleton from '@mui/material/Skeleton'; -import Grid from '@mui/material/Grid'; - -const variants = ['h1', 'h3', 'body1', 'caption'] as readonly TypographyProps['variant'][]; - -export default function ListFallbackComponent() { - return ( -
-
-
- - - -
-
- - - {variants.map((variant) => ( - - - - ))} - - -
-
-
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/index.tsx deleted file mode 100644 index 9a8709ea64..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockList/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { BlockListProps, useBlockList, withTextBlockManager } from './BlockList.hooks'; -import { withErrorBoundary } from 'react-error-boundary'; -import ListFallbackComponent from './ListFallbackComponent'; -import BlockListTitle from './BlockListTitle'; -import BlockComponent from '../BlockComponent'; -import BlockSelection from '../BlockSelection'; - -function BlockList(props: BlockListProps) { - const { root, rowVirtualizer, parentRef, blockEditor } = useBlockList(props); - - const virtualItems = rowVirtualizer.getVirtualItems(); - return ( -
-
-
- {root && virtualItems.length ? ( -
- {virtualItems.map((virtualRow) => { - const id = root.children[virtualRow.index].id; - return ( -
- {virtualRow.index === 0 ? : null} - -
- ); - })} -
- ) : null} -
-
- {parentRef.current ? : null} -
- ); -} - -const ListWithErrorBoundary = withErrorBoundary(withTextBlockManager(BlockList), { - FallbackComponent: ListFallbackComponent, -}); - -export default React.memo(ListWithErrorBoundary); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/index.tsx deleted file mode 100644 index 4ef554d489..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useBlockSelection } from './BlockSelection.hooks'; -import { BlockEditor } from '$app/block_editor'; -import React from 'react'; - -function BlockSelection({ container, blockEditor }: { container: HTMLDivElement; blockEditor: BlockEditor }) { - const { isDragging, style } = useBlockSelection({ - container, - blockEditor, - }); - - return ( -
- {isDragging ?
: null} -
- ); -} - -export default React.memo(BlockSelection); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/CodeBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/CodeBlock/index.tsx deleted file mode 100644 index eb34844d2c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/CodeBlock/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { BlockCommonProps } from '@/appflowy_app/interfaces'; - -export default function CodeBlock({ node }: BlockCommonProps) { - return
{node.data.text}
; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/HeadingBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/HeadingBlock/index.tsx deleted file mode 100644 index f0a1bd3323..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/HeadingBlock/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import TextBlock from '../TextBlock'; -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { BlockCommonProps } from '@/appflowy_app/interfaces'; - -const fontSize: Record = { - 1: 'mt-8 text-3xl', - 2: 'mt-6 text-2xl', - 3: 'mt-4 text-xl', -}; - -export default function HeadingBlock({ node, version }: BlockCommonProps) { - return ( -
- -
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/ColumnListBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/ColumnListBlock.tsx deleted file mode 100644 index ce0a1254d3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/ColumnListBlock.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import React, { useMemo } from 'react'; -import ColumnBlock from '../ColumnBlock'; - -export default function ColumnListBlock({ node }: { node: TreeNode }) { - const resizerWidth = useMemo(() => { - return 46 * (node.children?.length || 0); - }, [node.children?.length]); - return ( - <> -
- {node.children?.map((item, index) => ( - - ))} -
- - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/NumberedListBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/NumberedListBlock.tsx deleted file mode 100644 index 6bc63d41ef..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/NumberedListBlock.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import BlockComponent from '../BlockComponent'; -import { BlockType } from '@/appflowy_app/interfaces'; -import { Block } from '@/appflowy_app/block_editor/core/block'; - -export default function NumberedListBlock({ title, node }: { title: JSX.Element; node: TreeNode }) { - let prev = node.block.prev; - let index = 1; - while (prev && prev.type === BlockType.ListBlock && (prev as Block).data.type === 'numbered') { - index++; - prev = prev.prev; - } - return ( -
-
-
{`${index} .`}
- {title} -
- -
- {node.children?.map((item) => ( -
- -
- ))} -
-
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/PageBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/PageBlock/index.tsx deleted file mode 100644 index a79e036dbe..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/PageBlock/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { BlockCommonProps } from '@/appflowy_app/interfaces'; - -export default function PageBlock({ node }: BlockCommonProps) { - return
{node.data.title}
; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.hooks.ts deleted file mode 100644 index a776ae8be4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.hooks.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { TreeNode } from "@/appflowy_app/block_editor/view/tree_node"; -import { triggerHotkey } from "@/appflowy_app/utils/slate/hotkey"; -import { useCallback, useContext, useLayoutEffect, useState } from "react"; -import { Transforms, createEditor, Descendant } from 'slate'; -import { ReactEditor, withReact } from 'slate-react'; -import { TextBlockContext } from '$app/utils/slate/context'; - -export function useTextBlock({ - node, -}: { - node: TreeNode; -}) { - const [editor] = useState(() => withReact(createEditor())); - - const { textBlockManager } = useContext(TextBlockContext); - - const value = [ - { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - type: 'paragraph', - children: node.data.content, - }, - ]; - - - const onChange = useCallback( - (e: Descendant[]) => { - if (!editor.operations || editor.operations.length === 0) return; - if (editor.operations[0].type !== 'set_selection') { - console.log('====text block ==== ', editor.operations) - const children = 'children' in e[0] ? e[0].children : []; - textBlockManager?.update(node, ['data', 'content'], children); - } else { - const newProperties = editor.operations[0].newProperties; - textBlockManager?.setSelection(node, editor.selection); - } - }, - [node.id, editor], - ); - - - const onKeyDownCapture = (event: React.KeyboardEvent) => { - switch (event.key) { - case 'Enter': { - event.stopPropagation(); - event.preventDefault(); - textBlockManager?.splitNode(node, editor); - - return; - } - } - - triggerHotkey(event, editor); - } - - - - const { focusId, selection } = textBlockManager!.selectionManager.getFocusSelection(); - - editor.children = value; - Transforms.collapse(editor); - - useLayoutEffect(() => { - let timer: NodeJS.Timeout; - if (focusId === node.id && selection) { - ReactEditor.focus(editor); - Transforms.select(editor, selection); - // Use setTimeout to delay setting the selection - // until Slate has fully loaded and rendered all components and contents, - // to ensure that the operation succeeds. - timer = setTimeout(() => { - Transforms.select(editor, selection); - }, 100); - } - - return () => timer && clearTimeout(timer) - }, [editor]); - - const onDOMBeforeInput = useCallback((e: InputEvent) => { - // COMPAT: in Apple, `compositionend` is dispatched after the - // `beforeinput` for "insertFromComposition". It will cause repeated characters when inputting Chinese. - // Here, prevent the beforeInput event and wait for the compositionend event to take effect - if (e.inputType === 'insertFromComposition') { - e.preventDefault(); - } - - }, []); - - - return { - editor, - value, - onChange, - onKeyDownCapture, - onDOMBeforeInput, - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.tsx deleted file mode 100644 index 906e9a4060..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import BlockComponent from '../BlockComponent'; -import { Slate, Editable } from 'slate-react'; -import Leaf from './Leaf'; -import HoveringToolbar from '$app/components/HoveringToolbar'; -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { useTextBlock } from './index.hooks'; -import { BlockCommonProps, TextBlockToolbarProps } from '@/appflowy_app/interfaces'; -import { toolbarDefaultProps } from '@/appflowy_app/constants/toolbar'; - -export default function TextBlock({ - node, - needRenderChildren = true, - toolbarProps, - ...props -}: { - needRenderChildren?: boolean; - toolbarProps?: TextBlockToolbarProps; -} & BlockCommonProps & - React.HTMLAttributes) { - const { editor, value, onChange, onKeyDownCapture, onDOMBeforeInput } = useTextBlock({ node }); - const { showGroups } = toolbarProps || toolbarDefaultProps; - - return ( -
- - {showGroups.length > 0 && } - } - placeholder='Enter some text...' - /> - - {needRenderChildren && node.children.length > 0 ? ( -
- {node.children.map((item) => ( - - ))} -
- ) : null} -
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockPortal/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockPortal/index.tsx new file mode 100644 index 0000000000..49ede75648 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockPortal/index.tsx @@ -0,0 +1,9 @@ +import ReactDOM from 'react-dom'; + +const BlockPortal = ({ blockId, children }: { blockId: string; children: JSX.Element }) => { + const root = document.querySelectorAll(`[data-block-id="${blockId}"] > .block-overlay`)[0]; + + return typeof document === 'object' && root ? ReactDOM.createPortal(children, root) : null; +}; + +export default BlockPortal; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/BlockSelection.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockSelection.hooks.tsx similarity index 69% rename from frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/BlockSelection.hooks.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockSelection.hooks.tsx index 00bc05f2d1..0404fe42b8 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/BlockSelection/BlockSelection.hooks.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockSelection.hooks.tsx @@ -1,13 +1,25 @@ -import { BlockEditor } from '@/appflowy_app/block_editor'; import { useEffect, useRef, useState, useCallback, useMemo } from 'react'; +import { useAppDispatch } from '$app/stores/store'; +import { documentActions } from '@/appflowy_app/stores/reducers/document/slice'; -export function useBlockSelection({ container, blockEditor }: { container: HTMLDivElement; blockEditor: BlockEditor }) { - const blockPositionManager = blockEditor.renderTree.blockPositionManager; +export function useBlockSelection({ + container, + onDragging, +}: { + container: HTMLDivElement; + onDragging?: (_isDragging: boolean) => void; +}) { + const ref = useRef(null); + const disaptch = useAppDispatch(); const [isDragging, setDragging] = useState(false); const pointRef = useRef([]); const startScrollTopRef = useRef(0); + useEffect(() => { + onDragging?.(isDragging); + }, [isDragging]); + const [rect, setRect] = useState<{ startX: number; startY: number; @@ -62,7 +74,7 @@ export function useBlockSelection({ container, blockEditor }: { container: HTMLD const calcIntersectBlocks = useCallback( (clientX: number, clientY: number) => { - if (!isDragging || !blockPositionManager) return; + if (!isDragging) return; const [startX, startY] = pointRef.current; const endX = clientX + container.scrollLeft; const endY = clientY + container.scrollTop; @@ -73,22 +85,23 @@ export function useBlockSelection({ container, blockEditor }: { container: HTMLD endX, endY, }); - const selectedBlocks = blockPositionManager.getIntersectBlocks( - Math.min(startX, endX), - Math.min(startY, endY), - Math.max(startX, endX), - Math.max(startY, endY) + disaptch( + documentActions.changeSelectionByIntersectRect({ + startX: Math.min(startX, endX), + startY: Math.min(startY, endY), + endX: Math.max(startX, endX), + endY: Math.max(startY, endY), + }) ); - const ids = selectedBlocks.map((item) => item.id); - blockEditor.renderTree.updateSelections(ids); }, [isDragging] ); const handleDraging = useCallback( (e: MouseEvent) => { - if (!isDragging || !blockPositionManager) return; + if (!isDragging) return; e.preventDefault(); + e.stopPropagation(); calcIntersectBlocks(e.clientX, e.clientY); const { top, bottom } = container.getBoundingClientRect(); @@ -106,7 +119,7 @@ export function useBlockSelection({ container, blockEditor }: { container: HTMLD const handleDragEnd = useCallback( (e: MouseEvent) => { if (isPointInBlock(e.target as HTMLElement) && !isDragging) { - blockEditor.renderTree.updateSelections([]); + disaptch(documentActions.updateSelections([])); return; } if (!isDragging) return; @@ -119,19 +132,21 @@ export function useBlockSelection({ container, blockEditor }: { container: HTMLD ); useEffect(() => { - window.addEventListener('mousedown', handleDragStart); - window.addEventListener('mousemove', handleDraging); - window.addEventListener('mouseup', handleDragEnd); + if (!ref.current) return; + document.addEventListener('mousedown', handleDragStart); + document.addEventListener('mousemove', handleDraging); + document.addEventListener('mouseup', handleDragEnd); return () => { - window.removeEventListener('mousedown', handleDragStart); - window.removeEventListener('mousemove', handleDraging); - window.removeEventListener('mouseup', handleDragEnd); + document.removeEventListener('mousedown', handleDragStart); + document.removeEventListener('mousemove', handleDraging); + document.removeEventListener('mouseup', handleDragEnd); }; }, [handleDragStart, handleDragEnd, handleDraging]); return { isDragging, style, + ref, }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/index.tsx new file mode 100644 index 0000000000..0a3ac62a84 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/index.tsx @@ -0,0 +1,23 @@ +import { useBlockSelection } from './BlockSelection.hooks'; +import React from 'react'; + +function BlockSelection({ + container, + onDragging, +}: { + container: HTMLDivElement; + onDragging?: (_isDragging: boolean) => void; +}) { + const { isDragging, style, ref } = useBlockSelection({ + container, + onDragging, + }); + + return ( +
+ {isDragging ?
: null} +
+ ); +} + +export default React.memo(BlockSelection); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/BlockSideTools.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/BlockSideTools.hooks.tsx new file mode 100644 index 0000000000..c707e4c4e1 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/BlockSideTools.hooks.tsx @@ -0,0 +1,126 @@ +import { BlockType } from '@/appflowy_app/interfaces/document'; +import { useAppSelector } from '@/appflowy_app/stores/store'; +import { debounce } from '@/appflowy_app/utils/tool'; +import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { DocumentControllerContext } from '$app/stores/effects/document/document_controller'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import { v4 } from 'uuid'; + +export function useBlockSideTools({ container }: { container: HTMLDivElement }) { + const [nodeId, setHoverNodeId] = useState(''); + const ref = useRef(null); + const nodes = useAppSelector((state) => state.document.nodes); + const { insertAfter } = useController(); + + const handleMouseMove = useCallback((e: MouseEvent) => { + const { clientX, clientY } = e; + const x = clientX; + const y = clientY; + const id = getNodeIdByPoint(x, y); + if (!id) { + setHoverNodeId(''); + } else { + if ([BlockType.ColumnBlock].includes(nodes[id].type)) { + setHoverNodeId(''); + return; + } + setHoverNodeId(id); + } + }, []); + + const debounceMove = useMemo(() => debounce(handleMouseMove, 30), [handleMouseMove]); + + useEffect(() => { + const el = ref.current; + if (!el || !nodeId) return; + + const node = nodes[nodeId]; + if (!node) { + el.style.opacity = '0'; + el.style.zIndex = '-1'; + } else { + el.style.opacity = '1'; + el.style.zIndex = '1'; + el.style.top = '1px'; + if (node?.type === BlockType.HeadingBlock) { + if (node.data.style?.level === 1) { + el.style.top = '8px'; + } else if (node.data.style?.level === 2) { + el.style.top = '6px'; + } else { + el.style.top = '5px'; + } + } + } + }, [nodeId, nodes]); + + const handleAddClick = useCallback(() => { + if (!nodeId) return; + insertAfter(nodes[nodeId]); + }, [nodeId, nodes]); + + useEffect(() => { + container.addEventListener('mousemove', debounceMove); + return () => { + container.removeEventListener('mousemove', debounceMove); + }; + }, [debounceMove]); + + return { + nodeId, + ref, + handleAddClick, + }; +} + +function useController() { + const controller = useContext(DocumentControllerContext); + + const insertAfter = useCallback((node: Node) => { + const parentId = node.parent; + if (!parentId || !controller) return; + + controller.transact([ + () => { + const newNode = { + id: v4(), + delta: [], + type: BlockType.TextBlock, + }; + controller.insert(newNode, parentId, node.id); + }, + ]); + }, []); + + return { + insertAfter, + }; +} + +function getNodeIdByPoint(x: number, y: number) { + const viewportNodes = document.querySelectorAll('[data-block-id]'); + let node: { + el: Element; + rect: DOMRect; + } | null = null; + viewportNodes.forEach((el) => { + const rect = el.getBoundingClientRect(); + + if (rect.x + rect.width - 1 >= x && rect.y + rect.height - 1 >= y && rect.y <= y) { + if (!node || rect.y > node.rect.y) { + node = { + el, + rect, + }; + } + } + }); + return node + ? ( + node as { + el: Element; + rect: DOMRect; + } + ).el.getAttribute('data-block-id') + : null; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/index.tsx new file mode 100644 index 0000000000..cf2631f474 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideTools/index.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { useBlockSideTools } from './BlockSideTools.hooks'; +import AddIcon from '@mui/icons-material/Add'; +import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; +import Portal from '../BlockPortal'; +import { IconButton } from '@mui/material'; + +const sx = { height: 24, width: 24 }; + +export default function BlockSideTools(props: { container: HTMLDivElement }) { + const { nodeId, ref, handleAddClick } = useBlockSideTools(props); + + if (!nodeId) return null; + return ( + +
{ + // prevent toolbar from taking focus away from editor + e.preventDefault(); + }} + > + handleAddClick()} sx={sx}> + + + + + +
+
+ ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/index.tsx new file mode 100644 index 0000000000..b4a152a824 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/index.tsx @@ -0,0 +1,3 @@ +export default function CodeBlock({ id }: { id: string }) { + return
{id}
; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/ColumnBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnBlock/index.tsx similarity index 59% rename from frontend/appflowy_tauri/src/appflowy_app/components/block/ColumnBlock/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnBlock/index.tsx index 8a6298bb2b..cd12b16f06 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/ColumnBlock/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnBlock/index.tsx @@ -1,17 +1,7 @@ import React from 'react'; -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; +import NodeComponent from '../Node'; -import BlockComponent from '../BlockComponent'; - -export default function ColumnBlock({ - node, - resizerWidth, - index, -}: { - node: TreeNode; - resizerWidth: number; - index: number; -}) { +export default function ColumnBlock({ id, index, width }: { id: string; index: number; width: string }) { const renderResizer = () => { return (
@@ -35,15 +25,14 @@ export default function ColumnBlock({ renderResizer() )} - } + id={id} /> ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/DocumentTitle.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/DocumentTitle.hooks.ts new file mode 100644 index 0000000000..dc67320b26 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/DocumentTitle.hooks.ts @@ -0,0 +1,8 @@ +import { useSubscribeNode } from '../_shared/SubscribeNode.hooks'; +export function useDocumentTitle(id: string) { + const { node, delta } = useSubscribeNode(id); + return { + node, + delta + } +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/index.tsx new file mode 100644 index 0000000000..2a7815b536 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentTitle/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { useDocumentTitle } from './DocumentTitle.hooks'; +import TextBlock from '../TextBlock'; + +export default function DocumentTitle({ id }: { id: string }) { + const { node, delta } = useDocumentTitle(id); + if (!node) return null; + return ( +
+ +
+ ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/HeadingBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/HeadingBlock/index.tsx new file mode 100644 index 0000000000..186d98e51c --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/HeadingBlock/index.tsx @@ -0,0 +1,17 @@ +import TextBlock from '../TextBlock'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import { TextDelta } from '@/appflowy_app/interfaces/document'; + +const fontSize: Record = { + 1: 'mt-8 text-3xl', + 2: 'mt-6 text-2xl', + 3: 'mt-4 text-xl', +}; + +export default function HeadingBlock({ node, delta }: { node: Node; delta: TextDelta[] }) { + return ( +
+ +
+ ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/FormatButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/FormatButton.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/FormatButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/FormatButton.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/FormatIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/FormatIcon.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/FormatIcon.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/FormatIcon.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/index.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/index.hooks.ts similarity index 76% rename from frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/index.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/index.hooks.ts index 8319291046..ac512b536f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/index.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/index.hooks.ts @@ -1,11 +1,9 @@ import { useEffect, useRef } from 'react'; import { useFocused, useSlate } from 'slate-react'; import { calcToolbarPosition } from '@/appflowy_app/utils/slate/toolbar'; -import { TreeNode } from '$app/block_editor/view/tree_node'; -export function useHoveringToolbar({node}: { - node: TreeNode -}) { + +export function useHoveringToolbar(id: string) { const editor = useSlate(); const inFocus = useFocused(); const ref = useRef(null); @@ -13,7 +11,7 @@ export function useHoveringToolbar({node}: { useEffect(() => { const el = ref.current; if (!el) return; - const nodeRect = document.querySelector(`[data-block-id=${node.id}]`)?.getBoundingClientRect(); + const nodeRect = document.querySelector(`[data-block-id="${id}"]`)?.getBoundingClientRect(); if (!nodeRect) return; const position = calcToolbarPosition(editor, el, nodeRect); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/index.tsx similarity index 74% rename from frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/index.tsx index dcd502905f..a35588033c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/HoveringToolbar/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/HoveringToolbar/index.tsx @@ -1,14 +1,13 @@ import FormatButton from './FormatButton'; -import Portal from './Portal'; -import { TreeNode } from '$app/block_editor/view/tree_node'; +import Portal from '../BlockPortal'; import { useHoveringToolbar } from './index.hooks'; -const HoveringToolbar = ({ blockId, node }: { blockId: string; node: TreeNode }) => { - const { inFocus, ref, editor } = useHoveringToolbar({ node }); +const HoveringToolbar = ({ id }: { id: string }) => { + const { inFocus, ref, editor } = useHoveringToolbar(id); if (!inFocus) return null; return ( - +
@@ -14,10 +21,8 @@ export default function BulletedListBlock({ title, node }: { title: JSX.Element;
- {node.children?.map((item) => ( -
- -
+ {childIds?.map((item) => ( + ))}
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/ColumnListBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/ColumnListBlock.tsx new file mode 100644 index 0000000000..82fd423e9d --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/ColumnListBlock.tsx @@ -0,0 +1,23 @@ +import React, { useMemo } from 'react'; +import ColumnBlock from '../ColumnBlock'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; + +export default function ColumnListBlock({ node, childIds }: { node: Node; childIds?: string[] }) { + const resizerWidth = useMemo(() => { + return 46 * (node.children?.length || 0); + }, [node.children?.length]); + return ( + <> +
+ {childIds?.map((item, index) => ( + + ))} +
+ + ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/NumberedListBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/NumberedListBlock.tsx new file mode 100644 index 0000000000..5c66f61133 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/NumberedListBlock.tsx @@ -0,0 +1,30 @@ +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import NodeComponent from '../Node'; + +export default function NumberedListBlock({ + title, + node, + childIds, +}: { + title: JSX.Element; + node: Node; + childIds?: string[]; +}) { + const index = 1; + return ( +
+
+
{`${index} .`}
+ {title} +
+ +
+ {childIds?.map((item) => ( + + ))} +
+
+ ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/index.tsx similarity index 51% rename from frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/index.tsx index 87c31795ce..a33b36cbde 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/block/ListBlock/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ListBlock/index.tsx @@ -3,28 +3,28 @@ import TextBlock from '../TextBlock'; import NumberedListBlock from './NumberedListBlock'; import BulletedListBlock from './BulletedListBlock'; import ColumnListBlock from './ColumnListBlock'; -import { TreeNode } from '@/appflowy_app/block_editor/view/tree_node'; -import { BlockCommonProps } from '@/appflowy_app/interfaces'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import { TextDelta } from '@/appflowy_app/interfaces/document'; -export default function ListBlock({ node, version }: BlockCommonProps) { +export default function ListBlock({ node, delta }: { node: Node; delta: TextDelta[] }) { const title = useMemo(() => { - if (node.data.type === 'column') return <>; + if (node.data.style?.type === 'column') return <>; return (
- +
); - }, [node, version]); + }, [node, delta]); - if (node.data.type === 'numbered') { + if (node.data.style?.type === 'numbered') { return ; } - if (node.data.type === 'bulleted') { + if (node.data.style?.type === 'bulleted') { return ; } - if (node.data.type === 'column') { + if (node.data.style?.type === 'column') { return ; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Node/Node.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/Node/Node.hooks.ts new file mode 100644 index 0000000000..1bb2e2b25d --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/Node/Node.hooks.ts @@ -0,0 +1,36 @@ + +import { useEffect, useRef } from 'react'; +import { useSubscribeNode } from '../_shared/SubscribeNode.hooks'; +import { useAppDispatch } from '$app/stores/store'; +import { documentActions } from '$app/stores/reducers/document/slice'; + +export function useNode(id: string) { + const { node, childIds, delta, isSelected } = useSubscribeNode(id); + const ref = useRef(null); + + const dispatch = useAppDispatch(); + + useEffect(() => { + if (!ref.current) return; + const rect = ref.current.getBoundingClientRect(); + + const scrollContainer = document.querySelector('.doc-scroller-container') as HTMLDivElement; + dispatch(documentActions.updateNodePosition({ + id, + rect: { + x: rect.x, + y: rect.y + scrollContainer.scrollTop, + height: rect.height, + width: rect.width + } + })) + }, []) + + return { + ref, + node, + childIds, + delta, + isSelected + } +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Node/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/Node/index.tsx new file mode 100644 index 0000000000..bfe2e9649b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/Node/index.tsx @@ -0,0 +1,42 @@ +import React, { useCallback } from 'react'; +import { useNode } from './Node.hooks'; +import { withErrorBoundary } from 'react-error-boundary'; +import { ErrorBoundaryFallbackComponent } from '../_shared/ErrorBoundaryFallbackComponent'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import TextBlock from '../TextBlock'; +import { TextDelta } from '@/appflowy_app/interfaces/document'; + +function NodeComponent({ id, ...props }: { id: string } & React.HTMLAttributes) { + const { node, childIds, delta, isSelected, ref } = useNode(id); + + console.log('=====', id); + const renderBlock = useCallback((_props: { node: Node; childIds?: string[]; delta?: TextDelta[] }) => { + switch (_props.node.type) { + case 'text': + if (!_props.delta) return null; + return ; + default: + break; + } + }, []); + + if (!node) return null; + + return ( +
+ {renderBlock({ + node, + childIds, + delta, + })} +
+ {isSelected ?
: null} +
+ ); +} + +const NodeWithErrorBoundary = withErrorBoundary(NodeComponent, { + FallbackComponent: ErrorBoundaryFallbackComponent, +}); + +export default React.memo(NodeWithErrorBoundary); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Overlay/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/Overlay/index.tsx new file mode 100644 index 0000000000..62d15de804 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/Overlay/index.tsx @@ -0,0 +1,13 @@ +import React, { useState } from 'react'; +import BlockSideTools from '../BlockSideTools'; +import BlockSelection from '../BlockSelection'; + +export default function Overlay({ container }: { container: HTMLDivElement }) { + const [isDragging, setDragging] = useState(false); + return ( + <> + {isDragging ? null : } + + + ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Root.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Root.hooks.tsx new file mode 100644 index 0000000000..faf8df0897 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Root.hooks.tsx @@ -0,0 +1,16 @@ +import { DocumentData } from '$app/interfaces/document'; +import { useSubscribeNode } from '../_shared/SubscribeNode.hooks'; +import { useParseTree } from './Tree.hooks'; + +export function useRoot({ documentData }: { documentData: DocumentData }) { + const { rootId } = documentData; + + useParseTree(documentData); + + const { node: rootNode, childIds: rootChildIds } = useSubscribeNode(rootId); + + return { + node: rootNode, + childIds: rootChildIds, + }; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Tree.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Tree.hooks.tsx new file mode 100644 index 0000000000..1191705f0b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/Tree.hooks.tsx @@ -0,0 +1,23 @@ +import { useEffect } from 'react'; +import { DocumentData } from '$app/interfaces/document'; +import { useAppDispatch } from '@/appflowy_app/stores/store'; +import { documentActions } from '$app/stores/reducers/document/slice'; + +export function useParseTree(documentData: DocumentData) { + const dispatch = useAppDispatch(); + const { blocks, ytexts, yarrays } = documentData; + + useEffect(() => { + dispatch( + documentActions.createTree({ + nodes: blocks, + delta: ytexts, + children: yarrays, + }) + ); + + return () => { + dispatch(documentActions.clear()); + }; + }, [documentData]); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/index.tsx new file mode 100644 index 0000000000..3e89c1b31e --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/Root/index.tsx @@ -0,0 +1,32 @@ +import { DocumentData } from '@/appflowy_app/interfaces/document'; +import React, { useCallback } from 'react'; +import { useRoot } from './Root.hooks'; +import Node from '../Node'; +import { withErrorBoundary } from 'react-error-boundary'; +import { ErrorBoundaryFallbackComponent } from '../_shared/ErrorBoundaryFallbackComponent'; +import VirtualizerList from '../VirtualizerList'; +import { Skeleton } from '@mui/material'; + +function Root({ documentData }: { documentData: DocumentData }) { + const { node, childIds } = useRoot({ documentData }); + + const renderNode = useCallback((nodeId: string) => { + return ; + }, []); + + if (!node || !childIds) { + return ; + } + + return ( +
+ +
+ ); +} + +const RootWithErrorBoundary = withErrorBoundary(Root, { + FallbackComponent: ErrorBoundaryFallbackComponent, +}); + +export default React.memo(RootWithErrorBoundary); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/BindYjs.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/BindYjs.hooks.ts new file mode 100644 index 0000000000..01aa2d204f --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/BindYjs.hooks.ts @@ -0,0 +1,61 @@ + + +import { useEffect, useMemo, useRef } from "react"; +import { createEditor } from "slate"; +import { withReact } from "slate-react"; + +import * as Y from 'yjs'; +import { withYjs, YjsEditor, slateNodesToInsertDelta } from '@slate-yjs/core'; +import { Delta } from '@slate-yjs/core/dist/model/types'; +import { TextDelta } from '@/appflowy_app/interfaces/document'; + +const initialValue = [{ + type: 'paragraph', + children: [{ text: '' }], +}]; + +export function useBindYjs(delta: TextDelta[], update: (_delta: TextDelta[]) => void) { + const yTextRef = useRef(); + // Create a yjs document and get the shared type + const sharedType = useMemo(() => { + const ydoc = new Y.Doc() + const _sharedType = ydoc.get('content', Y.XmlText) as Y.XmlText; + + const insertDelta = slateNodesToInsertDelta(initialValue); + // Load the initial value into the yjs document + _sharedType.applyDelta(insertDelta); + + const yText = insertDelta[0].insert as Y.XmlText; + yTextRef.current = yText; + + return _sharedType; + }, []); + + const editor = useMemo(() => withYjs(withReact(createEditor()), sharedType), []); + + useEffect(() => { + YjsEditor.connect(editor); + return () => { + yTextRef.current = undefined; + YjsEditor.disconnect(editor); + } + }, [editor]); + + useEffect(() => { + const yText = yTextRef.current; + if (!yText) return; + + const textEventHandler = (event: Y.YTextEvent) => { + update(event.changes.delta as TextDelta[]); + } + yText.applyDelta(delta); + yText.observe(textEventHandler); + + return () => { + yText.unobserve(textEventHandler); + } + }, [delta]) + + + return { editor } +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/Leaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/Leaf.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/Leaf.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/Leaf.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/TextBlock.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/TextBlock.hooks.ts new file mode 100644 index 0000000000..85ad6aced0 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/TextBlock.hooks.ts @@ -0,0 +1,110 @@ +import { triggerHotkey } from "@/appflowy_app/utils/slate/hotkey"; +import { useCallback, useContext, useMemo, useRef, useState } from "react"; +import { Descendant, Range } from "slate"; +import { useBindYjs } from "./BindYjs.hooks"; +import { DocumentControllerContext } from '$app/stores/effects/document/document_controller'; +import { TextDelta } from '$app/interfaces/document'; +import { debounce } from "@/appflowy_app/utils/tool"; + +function useController(textId: string) { + const docController = useContext(DocumentControllerContext); + + const update = useCallback( + (delta: TextDelta[]) => { + docController?.yTextApply(textId, delta) + }, + [textId], + ); + const transact = useCallback( + (actions: (() => void)[]) => { + docController?.transact(actions) + }, + [textId], + ) + + return { + update, + transact + } +} + +function useTransact(textId: string) { + const pendingActions = useRef<(() => void)[]>([]); + const { update, transact } = useController(textId); + + const sendTransact = useCallback( + () => { + const actions = pendingActions.current; + transact(actions); + }, + [transact], + ) + + const debounceSendTransact = useMemo(() => debounce(sendTransact, 300), [transact]); + + const sendDelta = useCallback( + (delta: TextDelta[]) => { + const action = () => update(delta); + pendingActions.current.push(action); + debounceSendTransact() + }, + [update, debounceSendTransact], + ); + return { + sendDelta + } +} + +export function useTextBlock(text: string, delta: TextDelta[]) { + const { sendDelta } = useTransact(text); + + const { editor } = useBindYjs(delta, sendDelta); + const [value, setValue] = useState([]); + + const onChange = useCallback( + (e: Descendant[]) => { + setValue(e); + }, + [editor], + ); + + const onKeyDownCapture = (event: React.KeyboardEvent) => { + switch (event.key) { + case 'Enter': { + event.stopPropagation(); + event.preventDefault(); + + return; + } + case 'Backspace': { + if (!editor.selection) return; + const { anchor } = editor.selection; + const isCollapase = Range.isCollapsed(editor.selection); + if (isCollapase && anchor.offset === 0 && anchor.path.toString() === '0,0') { + event.stopPropagation(); + event.preventDefault(); + return; + } + } + } + triggerHotkey(event, editor); + } + + const onDOMBeforeInput = useCallback((e: InputEvent) => { + // COMPAT: in Apple, `compositionend` is dispatched after the + // `beforeinput` for "insertFromComposition". It will cause repeated characters when inputting Chinese. + // Here, prevent the beforeInput event and wait for the compositionend event to take effect + if (e.inputType === 'insertFromComposition') { + e.preventDefault(); + } + + }, []); + + return { + onChange, + onKeyDownCapture, + onDOMBeforeInput, + editor, + value + } +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/index.tsx new file mode 100644 index 0000000000..a64bd56990 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/index.tsx @@ -0,0 +1,46 @@ +import { Slate, Editable } from 'slate-react'; +import Leaf from './Leaf'; +import { useTextBlock } from './TextBlock.hooks'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import NodeComponent from '../Node'; +import HoveringToolbar from '../HoveringToolbar'; +import { TextDelta } from '@/appflowy_app/interfaces/document'; +import React from 'react'; + +function TextBlock({ + node, + childIds, + placeholder, + delta, + ...props +}: { + node: Node; + delta: TextDelta[]; + childIds?: string[]; + placeholder?: string; +} & React.HTMLAttributes) { + const { editor, value, onChange, onKeyDownCapture, onDOMBeforeInput } = useTextBlock(node.data.text!, delta); + + return ( +
+ + + } + placeholder={placeholder || 'Please enter some text...'} + /> + + {childIds && childIds.length > 0 ? ( +
+ {childIds.map((item) => ( + + ))} +
+ ) : null} +
+ ); +} + +export default React.memo(TextBlock); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/VirtualizerList.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/VirtualizerList.hooks.tsx new file mode 100644 index 0000000000..c0e543bf5f --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/VirtualizerList.hooks.tsx @@ -0,0 +1,21 @@ +import { useVirtualizer } from '@tanstack/react-virtual'; +import { useRef } from 'react'; + +const defaultSize = 60; + +export function useVirtualizerList(count: number) { + const parentRef = useRef(null); + + const rowVirtualizer = useVirtualizer({ + count, + getScrollElement: () => parentRef.current, + estimateSize: () => { + return defaultSize; + }, + }); + + return { + rowVirtualizer, + parentRef, + }; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/index.tsx new file mode 100644 index 0000000000..5b3253b299 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/VirtualizerList/index.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { useVirtualizerList } from './VirtualizerList.hooks'; +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import DocumentTitle from '../DocumentTitle'; +import Overlay from '../Overlay'; + +export default function VirtualizerList({ + childIds, + node, + renderNode, +}: { + childIds: string[]; + node: Node; + renderNode: (nodeId: string) => JSX.Element; +}) { + const { rowVirtualizer, parentRef } = useVirtualizerList(childIds.length); + + const virtualItems = rowVirtualizer.getVirtualItems(); + + return ( + <> +
+
+ {node && childIds && virtualItems.length ? ( +
+ {virtualItems.map((virtualRow) => { + const id = childIds[virtualRow.index]; + return ( +
+ {virtualRow.index === 0 ? : null} + {renderNode(id)} +
+ ); + })} +
+ ) : null} +
+
+ {parentRef.current ? : null} + + ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/ErrorBoundaryFallbackComponent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/ErrorBoundaryFallbackComponent.tsx new file mode 100644 index 0000000000..fc6851734c --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/ErrorBoundaryFallbackComponent.tsx @@ -0,0 +1,12 @@ +import { Alert } from '@mui/material'; +import { FallbackProps } from 'react-error-boundary'; + +export function ErrorBoundaryFallbackComponent({ error, resetErrorBoundary }: FallbackProps) { + return ( + +

Something went wrong:

+
{error.message}
+ +
+ ); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts new file mode 100644 index 0000000000..1b3b4b71c8 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts @@ -0,0 +1,32 @@ +import { Node } from '@/appflowy_app/stores/reducers/document/slice'; +import { useAppSelector } from '@/appflowy_app/stores/store'; +import { useMemo } from 'react'; +import { TextDelta } from '@/appflowy_app/interfaces/document'; + +export function useSubscribeNode(id: string) { + const node = useAppSelector(state => state.document.nodes[id]); + const childIds = useAppSelector(state => { + const childrenId = state.document.nodes[id]?.children; + if (!childrenId) return; + return state.document.children[childrenId]; + }); + const delta = useAppSelector(state => { + const deltaId = state.document.nodes[id]?.data?.text; + if (!deltaId) return; + return state.document.delta[deltaId]; + }); + const isSelected = useAppSelector(state => { + return state.document.selections?.includes(id) || false; + }); + + const memoizedNode = useMemo(() => node, [node?.id, node?.data, node?.parent, node?.type, node?.children]); + const memoizedChildIds = useMemo(() => childIds, [JSON.stringify(childIds)]); + const memoizedDelta = useMemo(() => delta, [JSON.stringify(delta)]); + + return { + node: memoizedNode, + childIds: memoizedChildIds, + delta: memoizedDelta, + isSelected + }; +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/constants/toolbar.ts b/frontend/appflowy_tauri/src/appflowy_app/constants/toolbar.ts index a0efb98d60..61c9a88e06 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/constants/toolbar.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/constants/toolbar.ts @@ -1,4 +1,3 @@ -import { TextBlockToolbarGroup } from "../interfaces"; export const iconSize = { width: 18, height: 18 }; @@ -24,16 +23,3 @@ export const command: Record = { key: '⌘ + Shift + S or ⌘ + Shift + X', }, }; - -export const toolbarDefaultProps = { - showGroups: [ - TextBlockToolbarGroup.ASK_AI, - TextBlockToolbarGroup.BLOCK_SELECT, - TextBlockToolbarGroup.ADD_LINK, - TextBlockToolbarGroup.COMMENT, - TextBlockToolbarGroup.TEXT_FORMAT, - TextBlockToolbarGroup.TEXT_COLOR, - TextBlockToolbarGroup.MENTION, - TextBlockToolbarGroup.MORE, - ], -}; \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts new file mode 100644 index 0000000000..0cfefe0d75 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts @@ -0,0 +1,31 @@ +// eslint-disable-next-line no-shadow +export enum BlockType { + PageBlock = 'page', + HeadingBlock = 'heading', + ListBlock = 'list', + TextBlock = 'text', + CodeBlock = 'code', + EmbedBlock = 'embed', + QuoteBlock = 'quote', + DividerBlock = 'divider', + MediaBlock = 'media', + TableBlock = 'table', + ColumnBlock = 'column' +} +export interface NestedBlock { + id: string; + type: BlockType; + data: Record; + parent: string | null; + children: string; +} +export interface TextDelta { + insert: string; + attributes?: Record; +} +export interface DocumentData { + rootId: string; + blocks: Record; + ytexts: Record; + yarrays: Record; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts index e6d0760f64..db6c7f48b3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts @@ -1,112 +1 @@ -import { Descendant } from "slate"; - -// eslint-disable-next-line no-shadow -export enum BlockType { - PageBlock = 'page', - HeadingBlock = 'heading', - ListBlock = 'list', - TextBlock = 'text', - CodeBlock = 'code', - EmbedBlock = 'embed', - QuoteBlock = 'quote', - DividerBlock = 'divider', - MediaBlock = 'media', - TableBlock = 'table', - ColumnBlock = 'column' - -} - -export type BlockData = T extends BlockType.TextBlock ? TextBlockData : -T extends BlockType.PageBlock ? PageBlockData : -T extends BlockType.HeadingBlock ? HeadingBlockData : -T extends BlockType.ListBlock ? ListBlockData : -T extends BlockType.ColumnBlock ? ColumnBlockData : any; - - -export interface BlockInterface { - id: string; - type: BlockType; - data: BlockData; - next: string | null; - firstChild: string | null; -} - - -export interface TextBlockData { - content: Descendant[]; -} - -interface PageBlockData { - title: string; -} - -interface ListBlockData extends TextBlockData { - type: 'numbered' | 'bulleted' | 'column'; -} - -interface HeadingBlockData extends TextBlockData { - level: number; -} - -interface ColumnBlockData { - ratio: string; -} - -// eslint-disable-next-line no-shadow -export enum TextBlockToolbarGroup { - ASK_AI, - BLOCK_SELECT, - ADD_LINK, - COMMENT, - TEXT_FORMAT, - TEXT_COLOR, - MENTION, - MORE -} -export interface TextBlockToolbarProps { - showGroups: TextBlockToolbarGroup[] -} - - -export interface BlockCommonProps { - version: number; - node: T; -} - -export interface BackendOp { - type: 'update' | 'insert' | 'remove' | 'move' | 'move_range'; - version: number; - data: UpdateOpData | InsertOpData | moveRangeOpData | moveOpData | removeOpData; -} -export interface LocalOp { - type: 'update' | 'insert' | 'remove' | 'move' | 'move_range'; - version: number; - data: UpdateOpData | InsertOpData | moveRangeOpData | moveOpData | removeOpData; -} - -export interface UpdateOpData { - blockId: string; - value: BlockData; - path: string[]; -} -export interface InsertOpData { - block: BlockInterface; - parentId: string; - prevId?: string -} - -export interface moveRangeOpData { - range: [string, string]; - newParentId: string; - newPrevId?: string -} - -export interface moveOpData { - blockId: string; - newParentId: string; - newPrevId?: string -} - -export interface removeOpData { - blockId: string -} \ No newline at end of file +export interface Document {} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/document_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/document_controller.ts new file mode 100644 index 0000000000..e6ee3ec250 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/document_controller.ts @@ -0,0 +1,50 @@ +import { DocumentData, BlockType, TextDelta } from '@/appflowy_app/interfaces/document'; +import { createContext } from 'react'; +import { DocumentBackendService } from './document_bd_svc'; +import { Err } from 'ts-results'; +import { FlowyError } from '@/services/backend'; + +export const DocumentControllerContext = createContext(null); + +export class DocumentController { + private readonly backendService: DocumentBackendService; + + constructor(public readonly viewId: string) { + this.backendService = new DocumentBackendService(viewId); + } + + open = async (): Promise => { + const openDocumentResult = await this.backendService.open(); + if (openDocumentResult.ok) { + return { + rootId: '', + blocks: {}, + ytexts: {}, + yarrays: {} + }; + } else { + return null; + } + }; + + + insert(node: { + id: string, + type: BlockType, + delta?: TextDelta[] + }, parentId: string, prevId: string) { + // + } + + transact(actions: (() => void)[]) { + // + } + + yTextApply = (yTextId: string, delta: TextDelta[]) => { + // + } + + dispose = async () => { + await this.backendService.close(); + }; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/region_grid.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/region_grid.ts similarity index 79% rename from frontend/appflowy_tauri/src/appflowy_app/block_editor/view/region_grid.ts rename to frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/region_grid.ts index 5f06f253ad..e7c7fd38ea 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/block_editor/view/region_grid.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/region_grid.ts @@ -14,6 +14,7 @@ interface BlockRegion { export class RegionGrid { private regions: BlockRegion[][]; private regionSize: number; + private blocks = new Map(); constructor(regionSize: number) { this.regionSize = regionSize; @@ -36,9 +37,22 @@ export class RegionGrid { } this.regions[regionY][regionX] = region; } - + this.blocks.set(blockPosition.id, blockPosition); region.blocks.push(blockPosition); } + + updateBlock(blockId: string, position: BlockPosition) { + const prevPosition = this.blocks.get(blockId); + if (prevPosition && prevPosition.x === position.x && + prevPosition.y === position.y && + prevPosition.height === position.height && + prevPosition.width === position.width) { + return; + } + this.blocks.set(blockId, position); + this.removeBlock(blockId); + this.addBlock(position); + } removeBlock(blockId: string) { for (const rows of this.regions) { @@ -51,6 +65,7 @@ export class RegionGrid { } } } + this.blocks.delete(blockId); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts new file mode 100644 index 0000000000..fbc8055d54 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts @@ -0,0 +1,132 @@ +import { BlockType, TextDelta } from "@/appflowy_app/interfaces/document"; +import { PayloadAction, createSlice } from "@reduxjs/toolkit"; +import { RegionGrid } from "./region_grid"; + +export interface Node { + id: string; + type: BlockType; + data: { + text?: string; + style?: Record + }; + parent: string | null; + children: string; +} + +export interface NodeState { + nodes: Record; + children: Record; + delta: Record; + selections: string[]; +} + +const regionGrid = new RegionGrid(50); + +const initialState: NodeState = { + nodes: {}, + children: {}, + delta: {}, + selections: [], +}; + +export const documentSlice = createSlice({ + name: 'document', + initialState: initialState, + reducers: { + clear: (state, action: PayloadAction) => { + return initialState; + }, + + createTree: (state, action: PayloadAction<{ + nodes: Record; + children: Record; + delta: Record; + }>) => { + const { nodes, children, delta } = action.payload; + state.nodes = nodes; + state.children = children; + state.delta = delta; + }, + + updateSelections: (state, action: PayloadAction) => { + state.selections = action.payload; + }, + + changeSelectionByIntersectRect: (state, action: PayloadAction<{ + startX: number; + startY: number; + endX: number; + endY: number + }>) => { + const { startX, startY, endX, endY } = action.payload; + const blocks = regionGrid.getIntersectBlocks(startX, startY, endX, endY); + state.selections = blocks.map(block => block.id); + }, + + updateNodePosition: (state, action: PayloadAction<{id: string; rect: { + x: number; + y: number; + width: number; + height: number; + }}>) => { + const { id, rect } = action.payload; + const position = { + id, + ...rect + }; + regionGrid.updateBlock(id, position); + }, + + addNode: (state, action: PayloadAction) => { + state.nodes[action.payload.id] = action.payload; + }, + + addChild: (state, action: PayloadAction<{ parentId: string, childId: string, prevId: string }>) => { + const { parentId, childId, prevId } = action.payload; + const parentChildrenId = state.nodes[parentId].children; + const children = state.children[parentChildrenId]; + const prevIndex = children.indexOf(prevId); + if (prevIndex === -1) { + children.push(childId) + } else { + children.splice(prevIndex + 1, 0, childId); + } + }, + + updateChildren: (state, action: PayloadAction<{ id: string; childIds: string[] }>) => { + const { id, childIds } = action.payload; + state.children[id] = childIds; + }, + + updateDelta: (state, action: PayloadAction<{ id: string; delta: TextDelta[] }>) => { + const { id, delta } = action.payload; + state.delta[id] = delta; + }, + + updateNode: (state, action: PayloadAction<{id: string; type?: BlockType; data?: any }>) => { + state.nodes[action.payload.id] = { + ...state.nodes[action.payload.id], + ...action.payload + } + }, + + removeNode: (state, action: PayloadAction) => { + const { children, data, parent } = state.nodes[action.payload]; + if (parent) { + const index = state.children[state.nodes[parent].children].indexOf(action.payload); + if (index > -1) { + state.children[state.nodes[parent].children].splice(index, 1); + } + } + if (children) { + delete state.children[children]; + } + if (data && data.text) { + delete state.delta[data.text]; + } + delete state.nodes[action.payload]; + }, + }, +}); + +export const documentActions = documentSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts index 1f96485938..9bd9c15909 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts @@ -14,6 +14,7 @@ import { currentUserSlice } from './reducers/current-user/slice'; import { gridSlice } from './reducers/grid/slice'; import { workspaceSlice } from './reducers/workspace/slice'; import { databaseSlice } from './reducers/database/slice'; +import { documentSlice } from './reducers/document/slice'; import { boardSlice } from './reducers/board/slice'; import { errorSlice } from './reducers/error/slice'; import { activePageIdSlice } from './reducers/activePageId/slice'; @@ -32,6 +33,7 @@ const store = configureStore({ [gridSlice.name]: gridSlice.reducer, [databaseSlice.name]: databaseSlice.reducer, [boardSlice.name]: boardSlice.reducer, + [documentSlice.name]: documentSlice.reducer, [workspaceSlice.name]: workspaceSlice.reducer, [errorSlice.name]: errorSlice.reducer, }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/block.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/block.ts deleted file mode 100644 index c40e840036..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/block.ts +++ /dev/null @@ -1,25 +0,0 @@ - -import { createContext } from 'react'; -import { ulid } from "ulid"; -import { BlockEditor } from '../block_editor/index'; - -export const BlockContext = createContext<{ - id?: string; - blockEditor?: BlockEditor; -}>({}); - - -export function generateBlockId() { - const blockId = ulid() - return `block-id-${blockId}`; -} - -const AVERAGE_BLOCK_HEIGHT = 30; -export function calculateViewportBlockMaxCount() { - const viewportHeight = window.innerHeight; - const viewportBlockCount = Math.ceil(viewportHeight / AVERAGE_BLOCK_HEIGHT); - - return viewportBlockCount; -} - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/block_selection.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/block_selection.ts deleted file mode 100644 index 8bc67522ce..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/block_selection.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { BlockData, BlockType } from "../interfaces"; - - -export function filterSelections(ids: string[], nodeMap: Map): string[] { - const selected = new Set(ids); - const newSelected = new Set(); - ids.forEach(selectedId => { - const node = nodeMap.get(selectedId); - if (!node) return; - if (node.type === BlockType.ListBlock && node.data.type === 'column') { - return; - } - if (node.children.length === 0) { - newSelected.add(selectedId); - return; - } - const hasChildSelected = node.children.some(i => selected.has(i.id)); - if (!hasChildSelected) { - newSelected.add(selectedId); - return; - } - const hasSiblingSelected = node.parent?.children.filter(i => i.id !== selectedId).some(i => selected.has(i.id)); - if (hasChildSelected && hasSiblingSelected) { - newSelected.add(selectedId); - return; - } - }); - - return Array.from(newSelected); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/slate/context.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/slate/context.ts deleted file mode 100644 index 387b74ff50..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/slate/context.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createContext } from "react"; -import { TextBlockManager } from '../../block_editor/blocks/text_block'; - -export const TextBlockContext = createContext<{ - textBlockManager?: TextBlockManager -}>({}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts index 88036d82d5..6bf8d0ebde 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts @@ -9,6 +9,21 @@ export function debounce(fn: (...args: any[]) => void, delay: number) { } } +export function throttle(fn: (...args: any[]) => void, delay: number, immediate = true) { + let timeout: NodeJS.Timeout | null = null + return (...args: any[]) => { + if (!timeout) { + timeout = setTimeout(() => { + timeout = null + // eslint-disable-next-line prefer-spread + !immediate && fn.apply(undefined, args) + }, delay) + // eslint-disable-next-line prefer-spread + immediate && fn.apply(undefined, args) + } + } +} + export function get(obj: any, path: string[], defaultValue?: any) { let value = obj; for (const prop of path) { @@ -34,3 +49,40 @@ export function set(obj: any, path: string[], value: any): void { } } } + +export function isEqual(value1: T, value2: T): boolean { + if (typeof value1 !== 'object' || value1 === null || typeof value2 !== 'object' || value2 === null) { + return value1 === value2; + } + + + if (Array.isArray(value1)) { + if (!Array.isArray(value2) || value1.length !== value2.length) { + return false; + } + + for (let i = 0; i < value1.length; i++) { + if (!isEqual(value1[i], value2[i])) { + return false; + } + } + + return true; + } + + const keys1 = Object.keys(value1); + const keys2 = Object.keys(value2); + + if (keys1.length !== keys2.length) { + return false; + } + + for (const key of keys1) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + if (!isEqual(value1[key], value2[key])) { + return false; + } + } + return true; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.hooks.ts index 1dfe73fd85..d9016e2586 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.hooks.ts @@ -4,796 +4,32 @@ import { DocumentVersionPB, OpenDocumentPayloadPB, } from '../../services/backend/events/flowy-document'; -import { BlockInterface, BlockType } from '../interfaces'; import { useParams } from 'react-router-dom'; -import { BlockEditor } from '../block_editor'; +import { DocumentData } from '../interfaces/document'; +import { DocumentController } from '$app/stores/effects/document/document_controller'; -const loadBlockData = async (id: string): Promise> => { - return { - [id]: { - id: id, - type: BlockType.PageBlock, - data: { content: [{ text: 'Document Title' }] }, - next: null, - firstChild: "L1-1", - }, - "L1-1": { - id: "L1-1", - type: BlockType.HeadingBlock, - data: { level: 1, content: [{ text: 'Heading 1' }] }, - next: "L1-2", - firstChild: null, - }, - "L1-2": { - id: "L1-2", - type: BlockType.HeadingBlock, - data: { level: 2, content: [{ text: 'Heading 2' }] }, - next: "L1-3", - firstChild: null, - }, - "L1-3": { - id: "L1-3", - type: BlockType.HeadingBlock, - data: { level: 3, content: [{ text: 'Heading 3' }] }, - next: "L1-4", - firstChild: null, - }, - "L1-4": { - id: "L1-4", - type: BlockType.TextBlock, - data: { content: [ - { - text: - 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ', - }, - { text: 'bold', bold: true }, - { text: ', ' }, - { text: 'italic', italic: true }, - { text: ', or anything else you might want to do!' }, - ] }, - next: "L1-5", - firstChild: null, - }, - "L1-5": { - id: "L1-5", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - { text: 'select any piece of text and the menu will appear', bold: true }, - { text: '.' }, - ] }, - next: "L1-6", - firstChild: "L1-5-1", - }, - "L1-5-1": { - id: "L1-5-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L1-5-2", - firstChild: null, - }, - "L1-5-2": { - id: "L1-5-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L1-6": { - id: "L1-6", - type: BlockType.ListBlock, - data: { type: 'bulleted', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - { text: 'bold', bold: true }, - { - text: - ', or add a semantically rendered block quote in the middle of the page, like this:', - }, - ] }, - next: "L1-7", - firstChild: "L1-6-1", - }, - "L1-6-1": { - id: "L1-6-1", - type: BlockType.ListBlock, - data: { type: 'numbered', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - - ] }, - - next: "L1-6-2", - firstChild: null, - }, - "L1-6-2": { - id: "L1-6-2", - type: BlockType.ListBlock, - data: { type: 'numbered', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - - ] }, - - next: "L1-6-3", - firstChild: null, - }, - "L1-6-3": { - id: "L1-6-3", - type: BlockType.TextBlock, - data: { content: [{ text: 'A wise quote.' }] }, - next: null, - firstChild: null, - }, - - "L1-7": { - id: "L1-7", - type: BlockType.ListBlock, - data: { type: 'column' }, - - next: "L1-8", - firstChild: "L1-7-1", - }, - "L1-7-1": { - id: "L1-7-1", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: "L1-7-2", - firstChild: "L1-7-1-1", - }, - "L1-7-1-1": { - id: "L1-7-1-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L1-7-2": { - id: "L1-7-2", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: "L1-7-3", - firstChild: "L1-7-2-1", - }, - "L1-7-2-1": { - id: "L1-7-2-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L1-7-2-2", - firstChild: null, - }, - "L1-7-2-2": { - id: "L1-7-2-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L1-7-3": { - id: "L1-7-3", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: null, - firstChild: "L1-7-3-1", - }, - "L1-7-3-1": { - id: "L1-7-3-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L1-8": { - id: "L1-8", - type: BlockType.HeadingBlock, - data: { level: 1, content: [{ text: 'Heading 1' }] }, - next: "L1-9", - firstChild: null, - }, - "L1-9": { - id: "L1-9", - type: BlockType.HeadingBlock, - data: { level: 2, content: [{ text: 'Heading 2' }] }, - next: "L1-10", - firstChild: null, - }, - "L1-10": { - id: "L1-10", - type: BlockType.HeadingBlock, - data: { level: 3, content: [{ text: 'Heading 3' }] }, - next: "L1-11", - firstChild: null, - }, - "L1-11": { - id: "L1-11", - type: BlockType.TextBlock, - data: { content: [ - { - text: - 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ', - }, - { text: 'bold', bold: true }, - { text: ', ' }, - { text: 'italic', italic: true }, - { text: ', or anything else you might want to do!' }, - ] }, - next: "L1-12", - firstChild: null, - }, - "L1-12": { - id: "L1-12", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - { text: 'select any piece of text and the menu will appear', bold: true }, - { text: '.' }, - ] }, - next: "L2-1", - firstChild: "L1-12-1", - }, - "L1-12-1": { - id: "L1-12-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L1-12-2", - firstChild: null, - }, - "L1-12-2": { - id: "L1-12-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L2-1": { - id: "L2-1", - type: BlockType.HeadingBlock, - data: { level: 1, content: [{ text: 'Heading 1' }] }, - next: "L2-2", - firstChild: null, - }, - "L2-2": { - id: "L2-2", - type: BlockType.HeadingBlock, - data: { level: 2, content: [{ text: 'Heading 2' }] }, - next: "L2-3", - firstChild: null, - }, - "L2-3": { - id: "L2-3", - type: BlockType.HeadingBlock, - data: { level: 3, content: [{ text: 'Heading 3' }] }, - next: "L2-4", - firstChild: null, - }, - "L2-4": { - id: "L2-4", - type: BlockType.TextBlock, - data: { content: [ - { - text: - 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ', - }, - { text: 'bold', bold: true }, - { text: ', ' }, - { text: 'italic', italic: true }, - { text: ', or anything else you might want to do!' }, - ] }, - next: "L2-5", - firstChild: null, - }, - "L2-5": { - id: "L2-5", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - { text: 'select any piece of text and the menu will appear', bold: true }, - { text: '.' }, - ] }, - next: "L2-6", - firstChild: "L2-5-1", - }, - "L2-5-1": { - id: "L2-5-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L2-5-2", - firstChild: null, - }, - "L2-5-2": { - id: "L2-5-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L2-6": { - id: "L2-6", - type: BlockType.ListBlock, - data: { type: 'bulleted', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - { text: 'bold', bold: true }, - { - text: - ', or add a semantically rendered block quote in the middle of the page, like this:', - }, - ] }, - next: "L2-7", - firstChild: "L2-6-1", - }, - "L2-6-1": { - id: "L2-6-1", - type: BlockType.ListBlock, - data: { type: 'numbered', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - - ] }, - - next: "L2-6-2", - firstChild: null, - }, - "L2-6-2": { - id: "L2-6-2", - type: BlockType.ListBlock, - data: { type: 'numbered', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - - ] }, - - next: "L2-6-3", - firstChild: null, - }, - - "L2-6-3": { - id: "L2-6-3", - type: BlockType.TextBlock, - data: { content: [{ text: 'A wise quote.' }] }, - next: null, - firstChild: null, - }, - - "L2-7": { - id: "L2-7", - type: BlockType.ListBlock, - data: { type: 'column' }, - - next: "L2-8", - firstChild: "L2-7-1", - }, - "L2-7-1": { - id: "L2-7-1", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: "L2-7-2", - firstChild: "L2-7-1-1", - }, - "L2-7-1-1": { - id: "L2-7-1-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L2-7-2": { - id: "L2-7-2", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: "L2-7-3", - firstChild: "L2-7-2-1", - }, - "L2-7-2-1": { - id: "L2-7-2-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L2-7-2-2", - firstChild: null, - }, - "L2-7-2-2": { - id: "L2-7-2-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L2-7-3": { - id: "L2-7-3", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: null, - firstChild: "L2-7-3-1", - }, - "L2-7-3-1": { - id: "L2-7-3-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L2-8": { - id: "L2-8", - type: BlockType.HeadingBlock, - data: { level: 1, content: [{ text: 'Heading 1' }] }, - next: "L2-9", - firstChild: null, - }, - "L2-9": { - id: "L2-9", - type: BlockType.HeadingBlock, - data: { level: 2, content: [{ text: 'Heading 2' }] }, - next: "L2-10", - firstChild: null, - }, - "L2-10": { - id: "L2-10", - type: BlockType.HeadingBlock, - data: { level: 3, content: [{ text: 'Heading 3' }] }, - next: "L2-11", - firstChild: null, - }, - "L2-11": { - id: "L2-11", - type: BlockType.TextBlock, - data: { content: [ - { - text: - 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ', - }, - { text: 'bold', bold: true }, - { text: ', ' }, - { text: 'italic', italic: true }, - { text: ', or anything else you might want to do!' }, - ] }, - next: "L2-12", - firstChild: null, - }, - "L2-12": { - id: "L2-12", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - { text: 'select any piece of text and the menu will appear', bold: true }, - { text: '.' }, - ] }, - next: "L3-1", - firstChild: "L2-12-1", - }, - "L2-12-1": { - id: "L2-12-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L2-12-2", - firstChild: null, - }, - "L2-12-2": { - id: "L2-12-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - },"L3-1": { - id: "L3-1", - type: BlockType.HeadingBlock, - data: { level: 1, content: [{ text: 'Heading 1' }] }, - next: "L3-2", - firstChild: null, - }, - "L3-2": { - id: "L3-2", - type: BlockType.HeadingBlock, - data: { level: 2, content: [{ text: 'Heading 2' }] }, - next: "L3-3", - firstChild: null, - }, - "L3-3": { - id: "L3-3", - type: BlockType.HeadingBlock, - data: { level: 3, content: [{ text: 'Heading 3' }] }, - next: "L3-4", - firstChild: null, - }, - "L3-4": { - id: "L3-4", - type: BlockType.TextBlock, - data: { content: [ - { - text: - 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ', - }, - { text: 'bold', bold: true }, - { text: ', ' }, - { text: 'italic', italic: true }, - { text: ', or anything else you might want to do!' }, - ] }, - next: "L3-5", - firstChild: null, - }, - "L3-5": { - id: "L3-5", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - { text: 'select any piece of text and the menu will appear', bold: true }, - { text: '.' }, - ] }, - next: "L3-6", - firstChild: "L3-5-1", - }, - "L3-5-1": { - id: "L3-5-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L3-5-2", - firstChild: null, - }, - "L3-5-2": { - id: "L3-5-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L3-6": { - id: "L3-6", - type: BlockType.ListBlock, - data: { type: 'bulleted', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - { text: 'bold', bold: true }, - { - text: - ', or add a semantically rendered block quote in the middle of the page, like this:', - }, - ] }, - next: "L3-7", - firstChild: "L3-6-1", - }, - "L3-6-1": { - id: "L3-6-1", - type: BlockType.ListBlock, - data: { type: 'numbered', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - - ] }, - - next: "L3-6-2", - firstChild: null, - }, - "L3-6-2": { - id: "L3-6-2", - type: BlockType.ListBlock, - data: { type: 'numbered', content: [ - { - text: - "Since it's rich text, you can do things like turn a selection of text ", - }, - - ] }, - - next: "L3-6-3", - firstChild: null, - }, - - "L3-6-3": { - id: "L3-6-3", - type: BlockType.TextBlock, - data: { content: [{ text: 'A wise quote.' }] }, - next: null, - firstChild: null, - }, - - "L3-7": { - id: "L3-7", - type: BlockType.ListBlock, - data: { type: 'column' }, - - next: "L3-8", - firstChild: "L3-7-1", - }, - "L3-7-1": { - id: "L3-7-1", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: "L3-7-2", - firstChild: "L3-7-1-1", - }, - "L3-7-1-1": { - id: "L3-7-1-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L3-7-2": { - id: "L3-7-2", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: "L3-7-3", - firstChild: "L3-7-2-1", - }, - "L3-7-2-1": { - id: "L3-7-2-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L3-7-2-2", - firstChild: null, - }, - "L3-7-2-2": { - id: "L3-7-2-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L3-7-3": { - id: "L3-7-3", - type: BlockType.ColumnBlock, - data: { ratio: '0.33' }, - next: null, - firstChild: "L3-7-3-1", - }, - "L3-7-3-1": { - id: "L3-7-3-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - "L3-8": { - id: "L3-8", - type: BlockType.HeadingBlock, - data: { level: 1, content: [{ text: 'Heading 1' }] }, - next: "L3-9", - firstChild: null, - }, - "L3-9": { - id: "L3-9", - type: BlockType.HeadingBlock, - data: { level: 2, content: [{ text: 'Heading 2' }] }, - next: "L3-10", - firstChild: null, - }, - "L3-10": { - id: "L3-10", - type: BlockType.HeadingBlock, - data: { level: 3, content: [{ text: 'Heading 3' }] }, - next: "L3-11", - firstChild: null, - }, - "L3-11": { - id: "L3-11", - type: BlockType.TextBlock, - data: { content: [ - { - text: - 'This example shows how you can make a hovering menu appear above your content, which you can use to make text ', - }, - { text: 'bold', bold: true }, - { text: ', ' }, - { text: 'italic', italic: true }, - { text: ', or anything else you might want to do!' }, - ] }, - next: "L3-12", - firstChild: null, - }, - "L3-12": { - id: "L3-12", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - { text: 'select any piece of text and the menu will appear', bold: true }, - { text: '.' }, - ] }, - next: null, - firstChild: "L3-12-1", - }, - "L3-12-1": { - id: "L3-12-1", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: "L3-12-2", - firstChild: null, - }, - "L3-12-2": { - id: "L3-12-2", - type: BlockType.TextBlock, - data: { content: [ - { text: 'Try it out yourself! Just ' }, - ] }, - next: null, - firstChild: null, - }, - } -} export const useDocument = () => { const params = useParams(); - const [blockId, setBlockId] = useState(); - const blockEditorRef = useRef(null) - + const [ documentId, setDocumentId ] = useState(); + const [ documentData, setDocumentData ] = useState(); + const [ controller, setController ] = useState(null); useEffect(() => { void (async () => { if (!params?.id) return; - const data = await loadBlockData(params.id); - console.log('==== enter ====', params?.id, data); - - if (!blockEditorRef.current) { - blockEditorRef.current = new BlockEditor(params?.id, data); - } else { - blockEditorRef.current.changeDoc(params?.id, data); - } - - setBlockId(params.id) + const c = new DocumentController(params.id); + setController(c); + const res = await c.open(); + console.log(res) + if (!res) return; + setDocumentData(res) + setDocumentId(params.id) + })(); return () => { console.log('==== leave ====', params?.id) } }, [params.id]); - return { blockId, blockEditor: blockEditorRef.current }; + return { documentId, documentData, controller }; }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx index 8ab2e71b07..301c241081 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx @@ -1,27 +1,23 @@ import { useDocument } from './DocumentPage.hooks'; -import BlockList from '../components/block/BlockList'; -import { BlockContext } from '../utils/block'; import { createTheme, ThemeProvider } from '@mui/material'; +import Root from '../components/document/Root'; +import { DocumentControllerContext } from '../stores/effects/document/document_controller'; const theme = createTheme({ typography: { fontFamily: ['Poppins'].join(','), }, }); -export const DocumentPage = () => { - const { blockId, blockEditor } = useDocument(); - if (!blockId || !blockEditor) return
; +export const DocumentPage = () => { + const { documentId, documentData, controller } = useDocument(); + + if (!documentId || !documentData || !controller) return null; return ( - - - + + + ); }; From 69dfb4a11ad63cf1fede801027646edb9a63d3b3 Mon Sep 17 00:00:00 2001 From: Imamuzzaki Abu Salam Date: Mon, 27 Mar 2023 20:20:50 +0700 Subject: [PATCH 09/37] docs : Fix for English Wording, Grammar, Spelling, and Clarity (#1805) * docs: fix for english wording, grammar, spelling * docs: revert congratulation paragraph * docs: rephrase line 79 Co-authored-by: Richard Shiue <71320345+richardshiue@users.noreply.github.com> --------- Co-authored-by: Richard Shiue <71320345+richardshiue@users.noreply.github.com> --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 93d6a11c70..5fa94d1b30 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ You are in charge of your data and customizations.

AppFlowy Github - how to star the repo

## Getting Started with development + Please view the [documentation](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy) for OS specific development instructions ## Roadmap -- [AppFlowy Roadmap ReadMe](https://appflowy.gitbook.io/docs/essential-documentation/roadmap) -- [AppFlowy Public Roadmap](https://github.com/orgs/AppFlowy-IO/projects/5/views/12) - +* [AppFlowy Roadmap ReadMe](https://appflowy.gitbook.io/docs/essential-documentation/roadmap) +* [AppFlowy Public Roadmap](https://github.com/orgs/AppFlowy-IO/projects/5/views/12) If you'd like to propose a feature, submit a feature request [here](https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&template=feature_request.yaml&title=%5BFR%5D+)
-If you'd like to report a bug, submit bug report [here](https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&template=bug_report.yaml&title=%5BBug%5D+) +If you'd like to report a bug, submit a bug report [here](https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&template=bug_report.yaml&title=%5BBug%5D+) ## **Releases** @@ -61,37 +61,37 @@ Please see the [changelog](https://www.appflowy.io/whatsnew) for more details ab ## Contributing -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. Please look at [Contributing to AppFlowy](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) for details. +Contributions make the open-source community a fantastic place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. Please look at [Contributing to AppFlowy](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) for details. -If your Pull Request is accepted as it fixes a bug, adds functionality, or makes AppFlowy's codebase significantly easier to use or understand, congratulations! If your administrative and managerial work behind the scenes that sustains the community as a whole, congratulations! You are now an official contributor to AppFlowy. Get in touch with us ([link](https://tally.so/r/mKP5z3)) to receive the very special Contributor T-shirt! +If your Pull Request is accepted as it fixes a bug, adds functionality, or makes AppFlowy's codebase significantly easier to use or understand, **Congratulations!** If your administrative and managerial work behind the scenes sustains the community, **Congratulations!** You are now an official contributor to AppFlowy. Get in touch with us ([link](https://tally.so/r/mKP5z3)) to receive the very special Contributor T-shirt! Proudly wear your T-shirt and show it to us by tagging [@appflowy](https://twitter.com/appflowy) on Twitter. +## Join the community to build AppFlowy together -## Join the community to build AppFlowy together! ## Why Are We Building This? -Notion has been our favorite project and knowledge management tool in recent years because of its aesthetic appeal and functionality. Our team uses it daily, and we are on its paid plan. However, as we all know Notion has its limitations. These include weak data security and poor compatibility with mobile devices. Likewise, alternative collaborative workplace management tools also have their constraints. +Notion has been our favourite project and knowledge management tool in recent years because of its aesthetic appeal and functionality. Our team uses it daily, and we are on its paid plan. However, as we all know, Notion has its limitations. These include weak data security and poor compatibility with mobile devices. Likewise, alternative collaborative workplace management tools also have their constraints. -The limitations we encountered using these tools rooted in our past work experience with collaborative productivity tools lead to our firm belief that there is, and will be a glass ceiling on what's possible in the future for tools like Notion. This emanates from these tools probable struggles to scale horizontally at some point. It implies that they will likely be forced to prioritize for a proportion of customers whose needs can be quite different from the rest. While decision-makers want a workplace OS, the truth is that it is not very possible to come up with a one-size fits all solution in such a fragmented market. +The limitations we encountered using these tools and our past work experience with collaborative productivity tools have led to our firm belief that there is a glass ceiling on what's possible for these tools in the future. This emanates from the fact that these tools will probably struggle to scale horizontally at some point and be forced to prioritize a proportion of customers whose needs differ from the rest. While decision-makers want a workplace OS, it is impossible to come up with a one-size fits all solution in such a fragmented market. When a customer's evolving core needs are not satisfied, they either switch to another or build one from the ground up, in-house. Consequently, they either go under another ceiling or buy an expensive ticket to learn a hard lesson. This is a requirement for many resources and expertise, building a reliable and easy-to-use collaborative tool, not to mention the speed and native experience. The same may apply to individual users as well. All these restrictions necessitate our mission - to make it possible for anyone to create apps that suit their needs well. -- To individuals, we would like to offer Notion's functionality along with data security and cross-platform native experience. -- To enterprises and hackers, AppFlowy is dedicated to offering building blocks, that is, collaboration infra services to enable you to make apps on your own. Moreover, you have 100% control of your data. You can design and modify AppFlowy your way, with a single codebase written in Flutter and Rust supporting multiple platforms armed with long-term maintainability. +* To individuals, we would like to offer Notion's functionality, data security, and cross-platform native experience. +* To enterprises and hackers, AppFlowy is dedicated to offering building blocks and collaboration infra services to enable you to make apps on your own. Moreover, you have 100% control of your data. You can design and modify AppFlowy your way, with a single codebase written in Flutter and Rust supporting multiple platforms armed with long-term maintainability. We decided to achieve this mission by upholding the three most fundamental values: -- Data privacy first -- Reliable native experience -- Community-driven extensibility +* Data privacy first +* Reliable native experience +* Community-driven extensibility -To be honest, we do not claim to outperform Notion in terms of functionality and design, at least for now. Besides, our priority doesn't lie in more functionality at the moment. Instead, we would like to cultivate a community to democratize the knowledge and wheels of making complex workplace management tools, while enabling people and businesses to create beautiful things on their own by equipping them with a versatile toolbox of building blocks. +We do not claim to outperform Notion in terms of functionality and design, at least for now. Besides, our priority doesn't lie in more functionality at the moment. Instead, we would like to cultivate a community to democratize the knowledge and wheels of making complex workplace management tools while enabling people and businesses to create beautiful things on their own by equipping them with a versatile toolbox of building blocks. ## License @@ -101,6 +101,6 @@ Distributed under the AGPLv3 License. See [`LICENSE.md`](https://github.com/AppF Special thanks to these amazing projects which help power AppFlowy.IO: -- [flutter-quill](https://github.com/singerdmx/flutter-quill) -- [cargo-make](https://github.com/sagiegurari/cargo-make) -- [contrib.rocks](https://contrib.rocks) +* [flutter-quill](https://github.com/singerdmx/flutter-quill) +* [cargo-make](https://github.com/sagiegurari/cargo-make) +* [contrib.rocks](https://contrib.rocks) From 9dc8e9d934a0ccf25b4d77aca4d691ffedd87900 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Tue, 28 Mar 2023 12:57:35 +0800 Subject: [PATCH 10/37] fix: modify same database by duplicate view (#2121) --- frontend/.vscode/launch.json | 4 ++-- frontend/rust-lib/flowy-database/src/manager.rs | 1 - .../src/services/database/database_editor.rs | 9 ++++++--- .../flowy-database/src/services/database_view/editor.rs | 2 +- .../src/services/database_view/editor_manager.rs | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json index 657f42fdd9..0501207968 100644 --- a/frontend/.vscode/launch.json +++ b/frontend/.vscode/launch.json @@ -12,8 +12,8 @@ "type": "dart", "preLaunchTask": "AF: Build Appflowy Core", "env": { - // "RUST_LOG": "trace", - "RUST_LOG": "debug" + "RUST_LOG": "trace", + // "RUST_LOG": "debug" }, "cwd": "${workspaceRoot}/appflowy_flutter" }, diff --git a/frontend/rust-lib/flowy-database/src/manager.rs b/frontend/rust-lib/flowy-database/src/manager.rs index 4bedef024d..f4b30de956 100644 --- a/frontend/rust-lib/flowy-database/src/manager.rs +++ b/frontend/rust-lib/flowy-database/src/manager.rs @@ -417,7 +417,6 @@ pub async fn create_new_database( DatabaseViewRevision::from_json(database_view_data)? }; - tracing::trace!("Initial calendar layout setting: {:?}", layout_setting); database_view_rev.layout_settings = layout_setting; let database_view_ops = make_database_view_operations(&database_view_rev); let database_view_bytes = database_view_ops.json_bytes(); diff --git a/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs index 217b5f6d22..b0ca679bdd 100644 --- a/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs @@ -888,9 +888,12 @@ impl DatabaseEditor { Ok(()) } - pub async fn duplicate_database(&self, view_id: &str) -> FlowyResult { + pub async fn duplicate_database(&self, _view_id: &str) -> FlowyResult { let database_pad = self.database_pad.read().await; - let database_view_data = self.database_views.duplicate_database_view(view_id).await?; + // let database_view_data = self + // .database_views + // .duplicate_database_view_setting(view_id) + // .await?; let original_blocks = database_pad.get_block_meta_revs(); let (duplicated_fields, duplicated_blocks) = database_pad.duplicate_database_block_meta().await; @@ -919,7 +922,7 @@ impl DatabaseEditor { block_metas: duplicated_blocks, blocks: blocks_meta_data, layout_setting: Default::default(), - database_view_data, + database_view_data: "".to_string(), }) } diff --git a/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs b/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs index 70188e5031..1d5449bd27 100644 --- a/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs @@ -238,7 +238,7 @@ impl DatabaseViewEditor { self.filter_controller.filter_row_revs(rows).await; } - pub async fn v_duplicate_data(&self) -> FlowyResult { + pub async fn v_duplicate_view_setting(&self) -> FlowyResult { let json_str = self.pad.read().await.json_str()?; Ok(json_str) } diff --git a/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs b/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs index cff486f1c5..993b5835e3 100644 --- a/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs +++ b/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs @@ -101,9 +101,9 @@ impl DatabaseViews { Ok(row_revs) } - pub async fn duplicate_database_view(&self, view_id: &str) -> FlowyResult { + pub async fn duplicate_database_view_setting(&self, view_id: &str) -> FlowyResult { let editor = self.get_view_editor(view_id).await?; - let view_data = editor.v_duplicate_data().await?; + let view_data = editor.v_duplicate_view_setting().await?; Ok(view_data) } From 5ef91a81574236f8e52dd535c7f2de66e185ec21 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Tue, 28 Mar 2023 07:46:49 +0200 Subject: [PATCH 11/37] fix: settings dialog files path copy snackbar (#2114) --- .../assets/translations/en.json | 3 +- .../settings/settings_dialog.dart | 65 ++++++++++++------- ...settings_file_customize_location_view.dart | 12 +++- .../lib/src/flowy_overlay/flowy_dialog.dart | 4 +- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/frontend/appflowy_flutter/assets/translations/en.json b/frontend/appflowy_flutter/assets/translations/en.json index 9496a242b7..bcca185f4e 100644 --- a/frontend/appflowy_flutter/assets/translations/en.json +++ b/frontend/appflowy_flutter/assets/translations/en.json @@ -198,7 +198,8 @@ "browser": "Browse", "create": "Create", "folderPath": "Path to store your folder", - "locationCannotBeEmpty": "Path cannot be empty" + "locationCannotBeEmpty": "Path cannot be empty", + "pathCopiedSnackbar": "File storage path copied to clipboard!" }, "user": { "name": "Name", 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 6f89f77a37..d7b3f35a17 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart @@ -12,6 +12,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +const _dialogHorizontalPadding = EdgeInsets.symmetric(horizontal: 12); +const _contentInsetPadding = EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0); + class SettingsDialog extends StatelessWidget { final UserProfilePB user; SettingsDialog(this.user, {Key? key}) : super(key: ValueKey(user.id)); @@ -23,34 +26,46 @@ class SettingsDialog extends StatelessWidget { ..add(const SettingsDialogEvent.initial()), child: BlocBuilder( builder: (context, state) => FlowyDialog( - title: FlowyText( - LocaleKeys.settings_title.tr(), - fontSize: 20, - fontWeight: FontWeight.w700, + title: Padding( + padding: _dialogHorizontalPadding + _contentInsetPadding, + child: FlowyText( + LocaleKeys.settings_title.tr(), + fontSize: 20, + fontWeight: FontWeight.w700, + ), ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 200, - child: SettingsMenu( - changeSelectedPage: (index) { - context - .read() - .add(SettingsDialogEvent.setSelectedPage(index)); - }, - currentPage: context.read().state.page, + child: ScaffoldMessenger( + child: Scaffold( + backgroundColor: Colors.transparent, + body: Padding( + padding: _dialogHorizontalPadding, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 200, + child: SettingsMenu( + changeSelectedPage: (index) { + context + .read() + .add(SettingsDialogEvent.setSelectedPage(index)); + }, + currentPage: + context.read().state.page, + ), + ), + const VerticalDivider(), + const SizedBox(width: 10), + Expanded( + child: getSettingsView( + context.read().state.page, + context.read().state.userProfile, + ), + ) + ], ), ), - const VerticalDivider(), - const SizedBox(width: 10), - Expanded( - child: getSettingsView( - context.read().state.page, - context.read().state.userProfile, - ), - ) - ], + ), ), ), ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart index 713e120092..8390b2a8af 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart @@ -46,7 +46,17 @@ class SettingsFileLocationCustomzierState onDoubleTap: () { Clipboard.setData(ClipboardData( text: state.path, - )); + )).then((_) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: FlowyText( + LocaleKeys.settings_files_pathCopiedSnackbar.tr(), + ), + ), + ); + } + }); }, child: FlowyText.regular( state.path ?? '', diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart index e0158d5c0c..f8cc85938b 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'dart:math'; -const _overlayContainerPadding = EdgeInsets.all(12); +const _overlayContainerPadding = EdgeInsets.symmetric(vertical: 12); const overlayContainerMaxWidth = 760.0; const overlayContainerMinWidth = 320.0; @@ -25,6 +25,7 @@ class FlowyDialog extends StatelessWidget { final windowSize = MediaQuery.of(context).size; final size = windowSize * 0.7; return SimpleDialog( + contentPadding: EdgeInsets.zero, title: title, shape: shape ?? RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), @@ -32,7 +33,6 @@ class FlowyDialog extends StatelessWidget { Material( type: MaterialType.transparency, child: Container( - padding: padding, height: size.height, width: max(min(size.width, overlayContainerMaxWidth), overlayContainerMinWidth), From f9ab6da4a35c1fe0e6b0e22deca7ae2e05c1f005 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Tue, 28 Mar 2023 14:27:14 +0800 Subject: [PATCH 12/37] fix: update calendar selected range (#2123) --- .../widgets/row/cells/date_cell/date_editor.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart index 0c0313c9fe..be6674b5bc 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart @@ -23,9 +23,8 @@ import '../../../../grid/presentation/widgets/common/type_option_separator.dart' import '../../../../grid/presentation/widgets/header/type_option/date.dart'; import 'date_cal_bloc.dart'; -final kToday = DateTime.now(); -final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); -final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); +final kFirstDay = DateTime.utc(1970, 1, 1); +final kLastDay = DateTime.utc(2100, 1, 1); class DateCellEditor extends StatefulWidget { final VoidCallback onDismissed; From 6ee54d6194086d501aa47492908b6736fcfb73d5 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 28 Mar 2023 15:26:35 +0800 Subject: [PATCH 13/37] fix: #2125 _AssertionError !_positions.contains(position): is not true (#2126) --- .../workspace/presentation/home/menu/app/section/section.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart index c134ca8985..b16d9b0e57 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/section.dart @@ -58,6 +58,7 @@ class ViewSection extends StatelessWidget { .read() .add(ViewSectionEvent.moveView(oldIndex, index)); }, + ignorePrimaryScrollController: true, children: children, ); } From 7b61ed228f77322a515b150cc05bf3a29b2acbc3 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 28 Mar 2023 16:30:26 +0800 Subject: [PATCH 14/37] fix: bold tests failed (#2122) --- .../appflowy_editor/documentation/testing.md | 8 +- .../selection_menu/selection_menu_widget.dart | 1 - .../lib/src/service/keyboard_service.dart | 2 - .../built_in_shortcut_events.dart | 4 +- .../test/infra/test_editor.dart | 11 ++- .../test/infra/test_raw_key_event.dart | 14 ++- .../render/rich_text/checkbox_text_test.dart | 8 +- .../selection_menu_widget_test.dart | 70 +++++++------- .../arrow_keys_handler_test.dart | 94 +++++++++---------- .../backspace_handler_test.dart | 50 +++++----- .../checkbox_event_handler_test.dart | 16 ++-- ...thout_shift_in_text_node_handler_test.dart | 16 ++-- .../exit_editing_mode_handler_test.dart | 2 +- .../format_style_handler_test.dart | 38 ++++---- .../markdown_syntax_to_styled_text_test.dart | 28 +++--- .../page_up_down_handler_test.dart | 8 +- .../redo_undo_handler_test.dart | 10 +- .../select_all_handler_test.dart | 6 +- .../slash_handler_test.dart | 5 +- .../space_on_web_handler_test.dart | 4 +- .../tab_handler_test.dart | 24 ++--- .../white_space_handler_test.dart | 34 +++---- .../shortcut_event/shortcut_event_test.dart | 22 ++--- 23 files changed, 242 insertions(+), 233 deletions(-) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md index 7e356d9f83..b388edddad 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md +++ b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md @@ -77,10 +77,10 @@ Next we will simulate the input of a shortcut key being pressed that will select ```dart // Meta + A. -await editor.pressLogicKey(LogicalKeyboardKey.keyA, isMetaPressed: true); +await editor.pressLogicKey(key: LogicalKeyboardKey.keyA, isMetaPressed: true); // Meta + shift + S. -await editor.pressLogicKey( - LogicalKeyboardKey.keyS, +await editor.pressLogicKey + key: LogicalKeyboardKey.keyS, isMetaPressed: true, isShiftPressed: true, ); @@ -130,7 +130,7 @@ void main() async { editor.insertTextNode(text); } await editor.startTesting(); - await editor.pressLogicKey(LogicalKeyboardKey.keyA, isMetaPressed: true); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyA, isMetaPressed: true); expect( editor.documentSelection, diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart index a212692e20..e8a4eca357 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart @@ -1,7 +1,6 @@ import 'dart:math'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart index 2068a2a0f0..bcd0f769fb 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart @@ -121,8 +121,6 @@ class _AppFlowyKeyboardState extends State return KeyEventResult.ignored; } - Log.keyboard.debug('on keyboard event $event'); - if (event is! RawKeyDownEvent) { return KeyEventResult.ignored; } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart index d9a0470f3d..3d8fa6839d 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -289,7 +289,7 @@ List builtInShortcutEvents = [ ), ShortcutEvent( key: 'Double tilde to strikethrough', - command: 'tilde,shift+tilde', + character: '~', handler: doubleTildeToStrikethrough, ), ShortcutEvent( @@ -314,7 +314,7 @@ List builtInShortcutEvents = [ ), ShortcutEvent( key: 'Double underscore to bold', - command: 'shift+underscore', + character: '_', handler: doubleUnderscoreToBoldHandler, ), // https://github.com/flutter/flutter/issues/104944 diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart index 69019f889c..910e101b63 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart @@ -120,8 +120,9 @@ class EditorWidgetTester { await tester.pumpAndSettle(); } - Future pressLogicKey( - LogicalKeyboardKey key, { + Future pressLogicKey({ + String? character, + LogicalKeyboardKey? key, bool isControlPressed = false, bool isShiftPressed = false, bool isAltPressed = false, @@ -130,11 +131,13 @@ class EditorWidgetTester { if (!isControlPressed && !isShiftPressed && !isAltPressed && - !isMetaPressed) { + !isMetaPressed && + key != null) { await tester.sendKeyDownEvent(key); } else { final testRawKeyEventData = TestRawKeyEventData( - logicalKey: key, + logicalKey: key ?? LogicalKeyboardKey.nonConvert, + character: character, isControlPressed: isControlPressed, isShiftPressed: isShiftPressed, isAltPressed: isAltPressed, diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart index 0a3d6cd74f..2b62a7022a 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; class TestRawKeyEvent extends RawKeyDownEvent { const TestRawKeyEvent({ required super.data, + required super.character, this.isControlPressed = false, this.isShiftPressed = false, this.isAltPressed = false, @@ -24,7 +25,9 @@ class TestRawKeyEvent extends RawKeyDownEvent { class TestRawKeyEventData extends RawKeyEventData { const TestRawKeyEventData({ - required this.logicalKey, + this.logicalKey = + LogicalKeyboardKey.nonConvert, // use nonConvert as placeholder. + this.character, this.isControlPressed = false, this.isShiftPressed = false, this.isAltPressed = false, @@ -46,6 +49,8 @@ class TestRawKeyEventData extends RawKeyEventData { @override final LogicalKeyboardKey logicalKey; + final String? character; + @override PhysicalKeyboardKey get physicalKey => logicalKey.toPhysicalKey; @@ -55,8 +60,10 @@ class TestRawKeyEventData extends RawKeyEventData { } @override - bool isModifierPressed(ModifierKey key, - {KeyboardSide side = KeyboardSide.any}) { + bool isModifierPressed( + ModifierKey key, { + KeyboardSide side = KeyboardSide.any, + }) { throw UnimplementedError(); } @@ -66,6 +73,7 @@ class TestRawKeyEventData extends RawKeyEventData { RawKeyEvent get toKeyEvent { return TestRawKeyEvent( data: this, + character: character, isAltPressed: isAltPressed, isControlPressed: isControlPressed, isMetaPressed: isMetaPressed, diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart index 576dabf7c4..b3e22f89e6 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart @@ -100,16 +100,16 @@ void main() async { Selection.single(path: [0], startOffset: text.length), ); - await editor.pressLogicKey(LogicalKeyboardKey.enter); - await editor.pressLogicKey(LogicalKeyboardKey.enter); - await editor.pressLogicKey(LogicalKeyboardKey.enter); + await editor.pressLogicKey(key: LogicalKeyboardKey.enter); + await editor.pressLogicKey(key: LogicalKeyboardKey.enter); + await editor.pressLogicKey(key: LogicalKeyboardKey.enter); expect( editor.documentSelection, Selection.single(path: [2], startOffset: 0), ); - await editor.pressLogicKey(LogicalKeyboardKey.slash); + await editor.pressLogicKey(key: LogicalKeyboardKey.slash); await tester.pumpAndSettle(const Duration(milliseconds: 1000)); expect( diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart index 45bbea120a..188663665e 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart @@ -1,5 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; @@ -22,10 +21,10 @@ void main() async { (tester) async { final editor = await _prepare(tester); for (var j = 0; j < i; j++) { - await editor.pressLogicKey(LogicalKeyboardKey.arrowDown); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); } - await editor.pressLogicKey(LogicalKeyboardKey.enter); + await editor.pressLogicKey(key: LogicalKeyboardKey.enter); expect( find.byType(SelectionMenuWidget, skipOffstage: false), findsNothing, @@ -52,33 +51,33 @@ void main() async { testWidgets('Search item in selection menu util no results', (tester) async { final editor = await _prepare(tester); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(3), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(5), ); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(3), ); - await editor.pressLogicKey(LogicalKeyboardKey.keyX); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyX); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(1), ); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(1), ); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNothing, @@ -88,13 +87,13 @@ void main() async { testWidgets('Search item in selection menu and presses esc', (tester) async { final editor = await _prepare(tester); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(3), ); - await editor.pressLogicKey(LogicalKeyboardKey.escape); + await editor.pressLogicKey(key: LogicalKeyboardKey.escape); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNothing, @@ -104,15 +103,15 @@ void main() async { testWidgets('Search item in selection menu and presses backspace', (tester) async { final editor = await _prepare(tester); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); + await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNWidgets(3), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), findsNothing, @@ -120,71 +119,70 @@ void main() async { }); group('tab and arrow keys move selection in desired direction', () { - testWidgets('left and right keys move selection in desired direction', - (tester) async { + (tester) async { final editor = await _prepare(tester); var initialSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - await editor.pressLogicKey(LogicalKeyboardKey.arrowRight); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowRight); var newSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(newSelection.item), 5); - await editor.pressLogicKey(LogicalKeyboardKey.arrowLeft); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowLeft); var finalSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); }); testWidgets('up and down keys move selection in desired direction', - (tester) async { + (tester) async { final editor = await _prepare(tester); var initialSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - await editor.pressLogicKey(LogicalKeyboardKey.arrowDown); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); var newSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(newSelection.item), 1); - await editor.pressLogicKey(LogicalKeyboardKey.arrowUp); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowUp); var finalSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(finalSelection.item), 0); }); - testWidgets('arrow keys and tab move same selection', - (tester) async { + testWidgets('arrow keys and tab move same selection', (tester) async { final editor = await _prepare(tester); var initialSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - await editor.pressLogicKey(LogicalKeyboardKey.arrowDown); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); var newSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(newSelection.item), 1); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); var finalSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(finalSelection.item), 6); }); - testWidgets('tab moves selection to next row Item on reaching end of current row', - (tester) async { + testWidgets( + 'tab moves selection to next row Item on reaching end of current row', + (tester) async { final editor = await _prepare(tester); final initialSelection = getSelectedMenuItem(tester); expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - await editor.pressLogicKey(LogicalKeyboardKey.tab); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); final finalSelection = getSelectedMenuItem(tester); @@ -203,7 +201,7 @@ Future _prepare(WidgetTester tester) async { } await editor.startTesting(); await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); - await editor.pressLogicKey(LogicalKeyboardKey.slash); + await editor.pressLogicKey(key: LogicalKeyboardKey.slash); await tester.pumpAndSettle(const Duration(milliseconds: 1000)); @@ -256,6 +254,6 @@ SelectionMenuItemWidget getSelectedMenuItem(WidgetTester tester) { return tester .state(find.byWidgetPredicate( (widget) => widget is SelectionMenuItemWidget && widget.isSelected, - )) + )) .widget as SelectionMenuItemWidget; -} \ No newline at end of file +} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart index 25e633e490..75b6696d29 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart @@ -25,7 +25,7 @@ void main() async { final textNode = editor.nodeAtPath([0]) as TextNode; for (var i = 0; i < text.length; i++) { - await editor.pressLogicKey(LogicalKeyboardKey.arrowRight); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowRight); if (i == text.length - 1) { // Wrap to next node if the cursor is at the end of the current node. @@ -73,7 +73,7 @@ void main() async { await editor.updateSelection(selection); for (var i = offset - 1; i >= 0; i--) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, ); expect( @@ -85,7 +85,7 @@ void main() async { } for (var i = text.length; i >= 0; i--) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, ); expect( @@ -97,7 +97,7 @@ void main() async { } for (var i = 1; i <= text.length; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, ); expect( @@ -109,7 +109,7 @@ void main() async { } for (var i = 0; i < text.length; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, ); expect( @@ -139,7 +139,7 @@ void main() async { await editor.updateSelection(selection); for (var i = end + 1; i <= text.length; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, ); expect( @@ -151,7 +151,7 @@ void main() async { } for (var i = text.length - 1; i >= 0; i--) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, ); expect( @@ -181,7 +181,7 @@ void main() async { await editor.updateSelection(selection); for (var i = end - 1; i >= 0; i--) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, ); expect( @@ -193,7 +193,7 @@ void main() async { } for (var i = 1; i <= text.length; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, ); expect( @@ -238,7 +238,7 @@ void main() async { await editor.updateSelection(selection); for (int i = 0; i < 3; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowUp, + key: LogicalKeyboardKey.arrowUp, isShiftPressed: true, ); } @@ -250,7 +250,7 @@ void main() async { ); for (int i = 0; i < 7; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowDown, + key: LogicalKeyboardKey.arrowDown, isShiftPressed: true, ); } @@ -262,7 +262,7 @@ void main() async { ); for (int i = 0; i < 3; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowUp, + key: LogicalKeyboardKey.arrowUp, isShiftPressed: true, ); } @@ -284,18 +284,18 @@ void main() async { final selection = Selection.single(path: [0], startOffset: 8); await editor.updateSelection(selection); await editor.pressLogicKey( - LogicalKeyboardKey.arrowDown, + key: LogicalKeyboardKey.arrowDown, isShiftPressed: true, ); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isMetaPressed: true, ); @@ -318,18 +318,18 @@ void main() async { final selection = Selection.single(path: [1], startOffset: 8); await editor.updateSelection(selection); await editor.pressLogicKey( - LogicalKeyboardKey.arrowUp, + key: LogicalKeyboardKey.arrowUp, isShiftPressed: true, ); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, isMetaPressed: true, ); @@ -352,7 +352,7 @@ void main() async { final selection = Selection.single(path: [1], startOffset: 10); await editor.updateSelection(selection); await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, isAltPressed: true, ); @@ -364,7 +364,7 @@ void main() async { ), ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, isAltPressed: true, ); @@ -376,7 +376,7 @@ void main() async { ), ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, isAltPressed: true, ); @@ -388,7 +388,7 @@ void main() async { ), ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isShiftPressed: true, isAltPressed: true, ); @@ -412,7 +412,7 @@ void main() async { final selection = Selection.single(path: [0], startOffset: 10); await editor.updateSelection(selection); await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isAltPressed: true, ); @@ -424,7 +424,7 @@ void main() async { ), ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isAltPressed: true, ); @@ -436,12 +436,12 @@ void main() async { ), ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isAltPressed: true, ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isAltPressed: true, ); @@ -453,7 +453,7 @@ void main() async { ), ); await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isShiftPressed: true, isAltPressed: true, ); @@ -478,12 +478,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isMetaPressed: true, ); } @@ -499,12 +499,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isMetaPressed: true, ); } @@ -521,12 +521,12 @@ void main() async { for (var i = 0; i < words.length; i++) { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isMetaPressed: true, ); } @@ -550,12 +550,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isMetaPressed: true, ); } @@ -575,12 +575,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, isMetaPressed: true, ); } @@ -610,11 +610,11 @@ Future _testPressArrowKeyInNotCollapsedSelection( end: isBackward ? end : start, ); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.arrowLeft); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowLeft); expect(editor.documentSelection?.start, start); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.arrowRight); + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowRight); expect(editor.documentSelection?.end, end); } @@ -652,12 +652,12 @@ Future _testPressArrowKeyWithMetaInSelection( await editor.updateSelection(selection); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isMetaPressed: true, ); } @@ -669,12 +669,12 @@ Future _testPressArrowKeyWithMetaInSelection( if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isMetaPressed: true, ); } @@ -686,12 +686,12 @@ Future _testPressArrowKeyWithMetaInSelection( if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowUp, + key: LogicalKeyboardKey.arrowUp, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowUp, + key: LogicalKeyboardKey.arrowUp, isMetaPressed: true, ); } @@ -703,12 +703,12 @@ Future _testPressArrowKeyWithMetaInSelection( if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowDown, + key: LogicalKeyboardKey.arrowDown, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowDown, + key: LogicalKeyboardKey.arrowDown, isMetaPressed: true, ); } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart index e786377de0..b299c18aa4 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart @@ -30,7 +30,7 @@ void main() async { // Pressing the backspace key continuously. for (int i = 1; i <= 1; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, ); expect(editor.documentLength, 1); expect(editor.documentSelection, @@ -112,7 +112,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [0], startOffset: text.length), ); - await editor.pressLogicKey(LogicalKeyboardKey.delete); + await editor.pressLogicKey(key: LogicalKeyboardKey.delete); expect(editor.documentLength, 1); expect(editor.documentSelection, @@ -207,7 +207,7 @@ void main() async { ), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 3); expect(find.byType(ImageNodeWidget), findsNothing); expect( @@ -249,20 +249,20 @@ void main() async { final textNode = editor.nodeAtPath([0]) as TextNode; await editor.insertText(textNode, '#', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect( (editor.nodeAtPath([0]) as TextNode).attributes.heading, BuiltInAttributeKey.h1, ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect( textNode.attributes.heading, null, ); await editor.insertText(textNode, '#', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect( (editor.nodeAtPath([0]) as TextNode).attributes.heading, BuiltInAttributeKey.h1, @@ -296,17 +296,17 @@ void main() async { await editor.updateSelection( Selection.single(path: [0, 0, 0], startOffset: 0), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.nodeAtPath([0, 0, 0])?.subtype, null); await editor.updateSelection( Selection.single(path: [0, 0, 0], startOffset: 0), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.nodeAtPath([0, 1]) != null, true); await editor.updateSelection( Selection.single(path: [0, 1], startOffset: 0), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.nodeAtPath([1]) != null, true); await editor.updateSelection( Selection.single(path: [1], startOffset: 0), @@ -314,7 +314,7 @@ void main() async { // * Welcome to Appflowy 😁 // * Welcome to Appflowy 😁Welcome to Appflowy 😁 - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect( editor.documentSelection, Selection.single(path: [0, 0], startOffset: text.length), @@ -356,7 +356,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [0, 1], startOffset: 0), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect( editor.nodeAtPath([0, 1])!.subtype != BuiltInAttributeKey.bulletedList, true, @@ -382,7 +382,7 @@ void main() async { // * Welcome to Appflowy 😁Welcome to Appflowy 😁 // * Welcome to Appflowy 😁 // * Welcome to Appflowy 😁 - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect( editor.nodeAtPath([0, 0])!.subtype == BuiltInAttributeKey.bulletedList, true, @@ -424,7 +424,7 @@ Future _deleteFirstImage(WidgetTester tester, bool isBackward) async { ), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 2); expect(find.byType(ImageNodeWidget), findsNothing); expect(editor.documentSelection, Selection.collapsed(start)); @@ -453,7 +453,7 @@ Future _deleteLastImage(WidgetTester tester, bool isBackward) async { ), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 2); expect(find.byType(ImageNodeWidget), findsNothing); expect(editor.documentSelection, Selection.collapsed(start)); @@ -483,12 +483,12 @@ Future _deleteStyledTextByBackspace( Selection.single(path: [2], startOffset: 0), ); await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, ); expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, ); expect(editor.documentLength, 2); expect(editor.documentSelection, @@ -500,7 +500,7 @@ Future _deleteStyledTextByBackspace( Selection.single(path: [1], startOffset: 0), ); await editor.pressLogicKey( - LogicalKeyboardKey.backspace, + key: LogicalKeyboardKey.backspace, ); expect(editor.documentLength, 2); expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0)); @@ -531,7 +531,7 @@ Future _deleteStyledTextByDelete( ); for (var i = 1; i < text.length; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.delete, + key: LogicalKeyboardKey.delete, ); expect( editor.documentSelection, Selection.single(path: [1], startOffset: 0)); @@ -541,7 +541,7 @@ Future _deleteStyledTextByDelete( } await editor.pressLogicKey( - LogicalKeyboardKey.delete, + key: LogicalKeyboardKey.delete, ); expect(editor.documentLength, 2); expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0)); @@ -562,7 +562,7 @@ Future _deleteTextByBackspace( await editor.updateSelection( Selection.single(path: [1], startOffset: 10), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 3); expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9)); @@ -573,7 +573,7 @@ Future _deleteTextByBackspace( await editor.updateSelection( Selection.single(path: [2], startOffset: 8, endOffset: 11), ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 3); expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8)); expect((editor.nodeAtPath([2]) as TextNode).toPlainText(), @@ -587,7 +587,7 @@ Future _deleteTextByBackspace( await editor.updateSelection(Selection( start: isBackwardSelection ? start : end, end: isBackwardSelection ? end : start)); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 1); expect( editor.documentSelection, Selection.single(path: [0], startOffset: 11)); @@ -608,7 +608,7 @@ Future _deleteTextByDelete( await editor.updateSelection( Selection.single(path: [1], startOffset: 9), ); - await editor.pressLogicKey(LogicalKeyboardKey.delete); + await editor.pressLogicKey(key: LogicalKeyboardKey.delete); expect(editor.documentLength, 3); expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9)); @@ -619,7 +619,7 @@ Future _deleteTextByDelete( await editor.updateSelection( Selection.single(path: [2], startOffset: 8, endOffset: 11), ); - await editor.pressLogicKey(LogicalKeyboardKey.delete); + await editor.pressLogicKey(key: LogicalKeyboardKey.delete); expect(editor.documentLength, 3); expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8)); expect((editor.nodeAtPath([2]) as TextNode).toPlainText(), @@ -633,7 +633,7 @@ Future _deleteTextByDelete( await editor.updateSelection(Selection( start: isBackwardSelection ? start : end, end: isBackwardSelection ? end : start)); - await editor.pressLogicKey(LogicalKeyboardKey.delete); + await editor.pressLogicKey(key: LogicalKeyboardKey.delete); expect(editor.documentLength, 1); expect( editor.documentSelection, Selection.single(path: [0], startOffset: 11)); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart index e1fd1f6ccc..03d5c78d04 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart @@ -47,12 +47,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isMetaPressed: true, ); } @@ -65,12 +65,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isMetaPressed: true, ); } @@ -145,12 +145,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isMetaPressed: true, ); } @@ -223,12 +223,12 @@ void main() async { if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, isMetaPressed: true, ); } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart index 74797b8402..9bca3748d6 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart @@ -26,7 +26,7 @@ void main() async { // Pressing the enter key continuously. for (int i = 1; i <= 10; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); expect(editor.documentLength, i + 1); expect(editor.documentSelection, @@ -64,7 +64,7 @@ void main() async { Selection.single(path: [lines - 1], startOffset: 0), ); await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); lines += 1; expect(editor.documentLength, lines); @@ -132,7 +132,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [0], startOffset: 0), ); - await editor.pressLogicKey(LogicalKeyboardKey.enter); + await editor.pressLogicKey(key: LogicalKeyboardKey.enter); expect(editor.documentLength, 2); expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text); }); @@ -159,7 +159,7 @@ Future _testStyleNeedToBeCopy(WidgetTester tester, String style) async { Selection.single(path: [1], startOffset: 0), ); await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); @@ -167,7 +167,7 @@ Future _testStyleNeedToBeCopy(WidgetTester tester, String style) async { Selection.single(path: [3], startOffset: text.length), ); await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); expect(editor.documentSelection, Selection.single(path: [4], startOffset: 0)); @@ -176,7 +176,7 @@ Future _testStyleNeedToBeCopy(WidgetTester tester, String style) async { expect(editor.nodeAtPath([4])?.subtype, null); await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); expect( editor.documentSelection, Selection.single(path: [5], startOffset: 0)); @@ -185,7 +185,7 @@ Future _testStyleNeedToBeCopy(WidgetTester tester, String style) async { expect(editor.nodeAtPath([4])?.subtype, style); await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); expect( editor.documentSelection, Selection.single(path: [4], startOffset: 0)); @@ -223,7 +223,7 @@ Future _testMultipleSelection( end: isBackwardSelection ? end : start, )); await editor.pressLogicKey( - LogicalKeyboardKey.enter, + key: LogicalKeyboardKey.enter, ); expect(editor.documentLength, 2); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart index 119bc652ae..0d264995cc 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart @@ -45,6 +45,6 @@ Future _testSelection( EditorWidgetTester editor, Selection selection) async { await editor.updateSelection(selection); expect(editor.documentSelection, selection); - await editor.pressLogicKey(LogicalKeyboardKey.escape); + await editor.pressLogicKey(key: LogicalKeyboardKey.escape); expect(editor.documentSelection, null); } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart index 222a7efe1b..842fcc6768 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart @@ -95,13 +95,13 @@ Future _testUpdateTextStyleByCommandX( await editor.updateSelection(selection); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isControlPressed: true, ); } else { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isMetaPressed: true, ); @@ -122,13 +122,13 @@ Future _testUpdateTextStyleByCommandX( await editor.updateSelection(selection); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isControlPressed: true, ); } else { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isMetaPressed: true, ); @@ -147,13 +147,13 @@ Future _testUpdateTextStyleByCommandX( await editor.updateSelection(selection); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isControlPressed: true, ); } else { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isMetaPressed: true, ); @@ -169,13 +169,13 @@ Future _testUpdateTextStyleByCommandX( await editor.updateSelection(selection); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isControlPressed: true, ); } else { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isMetaPressed: true, ); @@ -204,13 +204,13 @@ Future _testUpdateTextStyleByCommandX( if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isControlPressed: true, ); } else { await editor.pressLogicKey( - key, + key: key, isShiftPressed: isShiftPressed, isMetaPressed: true, ); @@ -250,9 +250,11 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { // trigger the link menu if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey(LogicalKeyboardKey.keyK, isControlPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyK, isControlPressed: true); } else { - await editor.pressLogicKey(LogicalKeyboardKey.keyK, isMetaPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyK, isMetaPressed: true); } expect(find.byType(LinkMenu), findsOneWidget); @@ -273,9 +275,11 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { await editor.updateSelection(selection); if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey(LogicalKeyboardKey.keyK, isControlPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyK, isControlPressed: true); } else { - await editor.pressLogicKey(LogicalKeyboardKey.keyK, isMetaPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyK, isMetaPressed: true); } expect(find.byType(LinkMenu), findsOneWidget); expect( @@ -290,9 +294,11 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { // Remove link if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey(LogicalKeyboardKey.keyK, isControlPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyK, isControlPressed: true); } else { - await editor.pressLogicKey(LogicalKeyboardKey.keyK, isMetaPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyK, isMetaPressed: true); } final removeLink = find.text('Remove link'); expect(removeLink, findsOneWidget); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart index 219fd07568..c7bb897034 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart @@ -17,7 +17,7 @@ void main() async { }) async { for (var i = 0; i < repeat; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.backquote, + key: LogicalKeyboardKey.backquote, ); } } @@ -99,7 +99,7 @@ void main() async { }) async { for (var i = 0; i < repeat; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.backquote, + key: LogicalKeyboardKey.backquote, ); } } @@ -157,10 +157,7 @@ void main() async { int repeat = 1, }) async { for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey( - LogicalKeyboardKey.tilde, - isShiftPressed: true, - ); + await editor.pressLogicKey(character: '~'); } } @@ -264,10 +261,7 @@ void main() async { int repeat = 1, }) async { for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey( - LogicalKeyboardKey.digit8, - isShiftPressed: true, - ); + await editor.pressLogicKey(character: '*'); } } @@ -358,10 +352,7 @@ void main() async { int repeat = 1, }) async { for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey( - LogicalKeyboardKey.underscore, - isShiftPressed: true, - ); + await editor.pressLogicKey(character: '_'); } } @@ -378,8 +369,13 @@ void main() async { await insertUnderscore(editor); - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 0, endOffset: textNode.toPlainText().length)); + final allBold = textNode.allSatisfyBoldInSelection( + Selection.single( + path: [0], + startOffset: 0, + endOffset: textNode.toPlainText().length, + ), + ); expect(allBold, true); expect(textNode.toPlainText(), 'AppFlowy'); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart index 465d198e8e..980d1ace20 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart @@ -38,7 +38,7 @@ void main() async { var currentOffsetY = 0.0; for (int i = 1; i <= page!; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.pageDown, + key: LogicalKeyboardKey.pageDown, ); if (i == page) { currentOffsetY = scrollService.maxScrollExtent; @@ -51,7 +51,7 @@ void main() async { for (int i = 1; i <= 5; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.pageDown, + key: LogicalKeyboardKey.pageDown, ); final dy = scrollService.dy; expect(dy == scrollService.maxScrollExtent, true); @@ -60,7 +60,7 @@ void main() async { // Pressing the pageUp key continuously. for (int i = page; i >= 1; i--) { await editor.pressLogicKey( - LogicalKeyboardKey.pageUp, + key: LogicalKeyboardKey.pageUp, ); if (i == 1) { currentOffsetY = scrollService.minScrollExtent; @@ -73,7 +73,7 @@ void main() async { for (int i = 1; i <= 5; i++) { await editor.pressLogicKey( - LogicalKeyboardKey.pageUp, + key: LogicalKeyboardKey.pageUp, ); final dy = scrollService.dy; expect(dy == scrollService.minScrollExtent, true); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart index 5e4260a4a1..d2a0bcbe2d 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart @@ -40,17 +40,17 @@ Future _testBackspaceUndoRedo( end: isDownwardSelection ? end : start, ); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.documentLength, 2); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.keyZ, + key: LogicalKeyboardKey.keyZ, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.keyZ, + key: LogicalKeyboardKey.keyZ, isMetaPressed: true, ); } @@ -61,13 +61,13 @@ Future _testBackspaceUndoRedo( if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.keyZ, + key: LogicalKeyboardKey.keyZ, isControlPressed: true, isShiftPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.keyZ, + key: LogicalKeyboardKey.keyZ, isMetaPressed: true, isShiftPressed: true, ); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart index 937988da58..25a38fd435 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart @@ -29,9 +29,11 @@ Future _testSelectAllHandler(WidgetTester tester, int lines) async { } await editor.startTesting(); if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey(LogicalKeyboardKey.keyA, isControlPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyA, isControlPressed: true); } else { - await editor.pressLogicKey(LogicalKeyboardKey.keyA, isMetaPressed: true); + await editor.pressLogicKey( + key: LogicalKeyboardKey.keyA, isMetaPressed: true); } expect( diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart index fd45401acf..d998022beb 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart @@ -1,5 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; @@ -20,7 +19,7 @@ void main() async { } await editor.startTesting(); await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); - await editor.pressLogicKey(LogicalKeyboardKey.slash); + await editor.pressLogicKey(key: LogicalKeyboardKey.slash); await tester.pumpAndSettle(const Duration(milliseconds: 1000)); @@ -53,7 +52,7 @@ void main() async { } await editor.startTesting(); await editor.updateSelection(Selection.single(path: [1], startOffset: 5)); - await editor.pressLogicKey(LogicalKeyboardKey.slash); + await editor.pressLogicKey(key: LogicalKeyboardKey.slash); await tester.pumpAndSettle(const Duration(milliseconds: 1000)); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart index a49d882fa3..4e326efa74 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart @@ -24,7 +24,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [i], startOffset: 1), ); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect( (editor.nodeAtPath([i]) as TextNode).toPlainText(), 'W elcome to Appflowy 😁', @@ -34,7 +34,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [i], startOffset: text.length + 1), ); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect( (editor.nodeAtPath([i]) as TextNode).toPlainText(), 'W elcome to Appflowy 😁 ', diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart index 4b88960c38..5ef746a2d0 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart @@ -18,7 +18,7 @@ void main() async { var selection = Selection.single(path: [0], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -27,7 +27,7 @@ void main() async { selection = Selection.single(path: [1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -61,7 +61,7 @@ void main() async { var selection = Selection.single(path: [0], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); // nothing happens expect( @@ -82,7 +82,7 @@ void main() async { selection = Selection.single(path: [1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -96,7 +96,7 @@ void main() async { selection = Selection.single(path: [1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -121,7 +121,7 @@ void main() async { document = editor.document; selection = Selection.single(path: [0, 0], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -131,7 +131,7 @@ void main() async { selection = Selection.single(path: [0, 1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -182,7 +182,7 @@ void main() async { var selection = Selection.single(path: [0], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); // nothing happens expect( @@ -203,7 +203,7 @@ void main() async { selection = Selection.single(path: [1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -216,7 +216,7 @@ void main() async { selection = Selection.single(path: [1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -239,7 +239,7 @@ void main() async { document = editor.document; selection = Selection.single(path: [0, 0], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, @@ -249,7 +249,7 @@ void main() async { selection = Selection.single(path: [0, 1], startOffset: 0); await editor.updateSelection(selection); - await editor.pressLogicKey(LogicalKeyboardKey.tab); + await editor.pressLogicKey(key: LogicalKeyboardKey.tab); expect( editor.documentSelection, diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart index 4349dc7a79..4418c4ed61 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart @@ -40,7 +40,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [i - 1], startOffset: i), ); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); final textNode = (editor.nodeAtPath([i - 1]) as TextNode); @@ -80,7 +80,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [i - 1], startOffset: i), ); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); final textNode = (editor.nodeAtPath([i - 1]) as TextNode); @@ -114,7 +114,7 @@ void main() async { final textNode = (editor.nodeAtPath([0]) as TextNode); await editor.insertText(textNode, '#' * i, 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.heading); // BuiltInAttributeKey.h2 ~ BuiltInAttributeKey.h6 @@ -134,7 +134,7 @@ void main() async { Selection.single(path: [0], startOffset: 0), ); await editor.insertText(textNode, symbol, 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, false); } @@ -152,7 +152,7 @@ void main() async { Selection.single(path: [0], startOffset: 0), ); await editor.insertText(textNode, symbol, 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, true); } @@ -169,7 +169,7 @@ void main() async { Selection.single(path: [0], startOffset: 0), ); await editor.insertText(textNode, symbol, 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.bulletedList); } }); @@ -185,34 +185,34 @@ void main() async { ); await editor.insertText(textNode, '>', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.quote); await editor.insertText(textNode, '*', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.bulletedList); await editor.insertText(textNode, '[]', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, false); await editor.insertText(textNode, '1.', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.numberList); await editor.insertText(textNode, '#', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.heading); await editor.insertText(textNode, '[x]', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, true); const insertedText = '[]AppFlowy'; await editor.insertText(textNode, insertedText, 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, true); expect(textNode.toPlainText(), insertedText); @@ -227,7 +227,7 @@ void main() async { await editor.updateSelection( Selection.single(path: [0], startOffset: text.length), ); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, null); expect(textNode.toPlainText(), text); }); @@ -243,7 +243,7 @@ void main() async { final textNode = editor.nodeAtPath([0]) as TextNode; await editor.insertText(textNode, '>', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.quote); for (var i = 0; i < text.length; i++) { await editor.insertText(textNode, text[i], i); @@ -263,7 +263,7 @@ void main() async { for (var i = 0; i < text.length; i++) { await editor.insertText(textNode, text[i], i); } - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); final isQuote = textNode.subtype == BuiltInAttributeKey.quote; expect(isQuote, false); expect(textNode.toPlainText(), text); @@ -284,7 +284,7 @@ void main() async { Selection.single(path: [0], startOffset: 0), ); await editor.insertText(textNode, '>', 0); - await editor.pressLogicKey(LogicalKeyboardKey.space); + await editor.pressLogicKey(key: LogicalKeyboardKey.space); final isQuote = textNode.subtype == BuiltInAttributeKey.quote; expect(isQuote, true); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart index 034d91a93e..a8a827140d 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart @@ -41,12 +41,12 @@ void main() async { ); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isMetaPressed: true, ); } @@ -69,12 +69,12 @@ void main() async { } if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isAltPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowLeft, + key: LogicalKeyboardKey.arrowLeft, isMetaPressed: true, ); } @@ -97,12 +97,12 @@ void main() async { ); if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isControlPressed: true, ); } else { await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isMetaPressed: true, ); } @@ -124,7 +124,7 @@ void main() async { } } await editor.pressLogicKey( - LogicalKeyboardKey.arrowRight, + key: LogicalKeyboardKey.arrowRight, isAltPressed: true, ); expect( @@ -145,7 +145,7 @@ void main() async { ); if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { await editor.pressLogicKey( - LogicalKeyboardKey.home, + key: LogicalKeyboardKey.home, ); } @@ -168,7 +168,7 @@ void main() async { } if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.home, + key: LogicalKeyboardKey.home, ); } expect( @@ -188,7 +188,7 @@ void main() async { ); if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { await editor.pressLogicKey( - LogicalKeyboardKey.end, + key: LogicalKeyboardKey.end, ); } @@ -211,7 +211,7 @@ void main() async { } if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { await editor.pressLogicKey( - LogicalKeyboardKey.end, + key: LogicalKeyboardKey.end, ); } expect( From a593c5607054c97bbc4b6581ba8737adb00a2464 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Tue, 28 Mar 2023 16:42:16 +0800 Subject: [PATCH 15/37] fix: tauri cell update (#2124) * test: subscribe text cell change * test: add edit url cell test --- .../components/tests/DatabaseTestHelper.ts | 12 +++ .../appflowy_app/components/tests/TestAPI.tsx | 4 + .../components/tests/TestGrid.tsx | 76 ++++++++++++++++--- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts index fdeb396073..f3b0f175e4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts @@ -16,6 +16,7 @@ import { NumberCellController, SelectOptionCellController, TextCellController, + URLCellController, } from '../../stores/effects/database/cell/controller_builder'; import { None, Option, Some } from 'ts-results'; import { TypeOptionBackendService } from '../../stores/effects/database/field/type_option/type_option_bd_svc'; @@ -125,6 +126,17 @@ export async function makeDateCellController( return Some(builder.build() as DateCellController); } +export async function makeURLCellController( + fieldId: string, + rowInfo: RowInfo, + databaseController: DatabaseController +): Promise> { + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.DateTime, databaseController).then( + (result) => result.unwrap() + ); + return Some(builder.build() as URLCellController); +} + export async function makeCellControllerBuilder( fieldId: string, rowInfo: RowInfo, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx index 464fdbf959..a45d82fa38 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx @@ -9,6 +9,8 @@ import { TestDeleteRow, TestEditCell, TestEditField, + TestEditTextCell, + TestEditURLCell, TestGetSingleSelectFieldData, TestSwitchFromMultiSelectToText, TestSwitchFromSingleSelectToNumber, @@ -33,6 +35,8 @@ export const TestAPI = () => { + + diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx index 075c7d04e9..3f7cf70341 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx @@ -5,8 +5,8 @@ import { NumberTypeOptionPB, SelectOptionCellDataPB, ViewLayoutTypePB, -} from '../../../services/backend'; -import { Log } from '../../utils/log'; +} from '@/services/backend'; +import { Log } from '$app/utils/log'; import { assertFieldName, assertNumberOfFields, @@ -19,18 +19,19 @@ import { makeMultiSelectCellController, makeSingleSelectCellController, makeTextCellController, + makeURLCellController, openTestDatabase, } from './DatabaseTestHelper'; -import { SelectOptionCellBackendService } from '../../stores/effects/database/cell/select_option_bd_svc'; -import { TypeOptionController } from '../../stores/effects/database/field/type_option/type_option_controller'; +import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; +import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; import { None, Some } from 'ts-results'; -import { RowBackendService } from '../../stores/effects/database/row/row_bd_svc'; -import { makeNumberTypeOptionContext } from '../../stores/effects/database/field/type_option/type_option_context'; +import { RowBackendService } from '$app/stores/effects/database/row/row_bd_svc'; +import { makeNumberTypeOptionContext } from '$app/stores/effects/database/field/type_option/type_option_context'; export const RunAllGridTests = () => { async function run() { await createBuildInGrid(); - await testEditGridRow(); + await testEditGridCell(); await testCreateRow(); await testDeleteRow(); await testCreateOptionInCell(); @@ -75,7 +76,7 @@ async function createBuildInGrid() { await databaseController.dispose(); } -async function testEditGridRow() { +async function testEditGridCell() { const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); const databaseController = await openTestDatabase(view.id); await databaseController.open().then((result) => result.unwrap()); @@ -88,6 +89,55 @@ async function testEditGridRow() { } } +async function testEditTextCell() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + const row = databaseController.databaseViewCache.rowInfos[0]; + const textField = findFirstFieldInfoWithFieldType(row, FieldType.RichText).unwrap(); + const textCellController = await makeTextCellController(textField.field.id, row, databaseController).then((result) => + result.unwrap() + ); + + textCellController.subscribeChanged({ + onCellChanged: (content) => { + Log.info('Receive text:', content); + }, + }); + + await textCellController.saveCellData('hello react'); + await new Promise((resolve) => setTimeout(resolve, 200)); + await databaseController.dispose(); +} + +async function testEditURLCell() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + const typeOptionController = new TypeOptionController(view.id, None, FieldType.URL); + await typeOptionController.initialize(); + + const row = databaseController.databaseViewCache.rowInfos[0]; + const urlCellController = await makeURLCellController(typeOptionController.fieldId, row, databaseController).then( + (result) => result.unwrap() + ); + + urlCellController.subscribeChanged({ + onCellChanged: (content) => { + const pb = content.unwrap(); + Log.info('Receive url data:', pb.url, pb.content); + }, + }); + + await urlCellController.saveCellData('hello react'); + await new Promise((resolve) => setTimeout(resolve, 200)); + + await urlCellController.saveCellData('appflowy.io'); + await new Promise((resolve) => setTimeout(resolve, 200)); +} + async function testCreateRow() { const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); const databaseController = await openTestDatabase(view.id); @@ -129,6 +179,7 @@ async function testCreateOptionInCell() { const cellController = await makeSingleSelectCellController(fieldInfo.field.id, row, databaseController).then( (result) => result.unwrap() ); + // eslint-disable-next-line @typescript-eslint/await-thenable await cellController.subscribeChanged({ onCellChanged: (value) => { if (value.some) { @@ -299,9 +350,16 @@ export const TestCreateGrid = () => { }; export const TestEditCell = () => { - return TestButton('Test editing cell', testEditGridRow); + return TestButton('Test editing cell', testEditGridCell); }; +export const TestEditTextCell = () => { + return TestButton('Test editing text cell', testEditTextCell); +}; + +export const TestEditURLCell = () => { + return TestButton('Test editing URL cell', testEditURLCell); +}; export const TestCreateRow = () => { return TestButton('Test create row', testCreateRow); }; From bdf7f37dc594551fddcfe6992adb62076b939784 Mon Sep 17 00:00:00 2001 From: Samiksha Garg <79906086+Samiksha-Garg@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:42:01 +0530 Subject: [PATCH 16/37] feat: improved tooltip of toolbar items (#2132) --- .../lib/src/render/toolbar/toolbar_item.dart | 29 ++++++++++++++----- .../render/toolbar/toolbar_item_widget.dart | 1 + 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart index 844ab4b2b6..ebf220065d 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart @@ -8,6 +8,7 @@ import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/extensions/editor_state_extensions.dart'; import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; +import 'dart:io' show Platform; import 'package:flutter/material.dart' hide Overlay, OverlayEntry; @@ -127,7 +128,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.bold', type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.bold, + tooltipsMessage: AppFlowyEditorLocalizations.current.bold + + "\n" + + (Platform.isMacOS ? "⌘ + B" : "CTRL + B"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/bold', color: isHighlight ? Colors.lightBlue : null, @@ -143,7 +146,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.italic', type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.italic, + tooltipsMessage: AppFlowyEditorLocalizations.current.italic + + "\n" + + (Platform.isMacOS ? "⌘ + I" : "CTRL + I"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/italic', color: isHighlight ? Colors.lightBlue : null, @@ -159,7 +164,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.underline', type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.underline, + tooltipsMessage: AppFlowyEditorLocalizations.current.underline + + "\n" + + (Platform.isMacOS ? "⌘ + U" : "CTRL + U"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/underline', color: isHighlight ? Colors.lightBlue : null, @@ -175,7 +182,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.strikethrough', type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.strikethrough, + tooltipsMessage: AppFlowyEditorLocalizations.current.strikethrough + + "\n" + + (Platform.isMacOS ? "⌘ + SHIFT + S" : "CTRL + SHIFT + S"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/strikethrough', color: isHighlight ? Colors.lightBlue : null, @@ -191,7 +200,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.code', type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.embedCode, + tooltipsMessage: AppFlowyEditorLocalizations.current.embedCode + + "\n" + + (Platform.isMacOS ? "⌘ + E" : "CTRL + E"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/code', color: isHighlight ? Colors.lightBlue : null, @@ -241,7 +252,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.link', type: 4, - tooltipsMessage: AppFlowyEditorLocalizations.current.link, + tooltipsMessage: AppFlowyEditorLocalizations.current.link + + "\n" + + (Platform.isMacOS ? "⌘ + K" : "CTRL + K"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/link', color: isHighlight ? Colors.lightBlue : null, @@ -257,7 +270,9 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.highlight', type: 4, - tooltipsMessage: AppFlowyEditorLocalizations.current.highlight, + tooltipsMessage: AppFlowyEditorLocalizations.current.highlight + + "\n" + + (Platform.isMacOS ? "⌘ + SHIFT + H" : "CTRL + SHIFT + H"), iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/highlight', color: isHighlight ? Colors.lightBlue : null, diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart index 85b1597564..22bcfab54e 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart @@ -21,6 +21,7 @@ class ToolbarItemWidget extends StatelessWidget { width: 28, height: 28, child: Tooltip( + textAlign: TextAlign.center, preferBelow: false, message: item.tooltipsMessage, child: MouseRegion( From 8541ca8becf458333beaadb19bbb85fb583ce9b4 Mon Sep 17 00:00:00 2001 From: Yijing Huang Date: Wed, 29 Mar 2023 20:44:37 -0500 Subject: [PATCH 17/37] Feat(appflowy_flutter): dark mode improvement for generic area/widgets (#2099) * chore: update color scheme for dark mode * chore: update dark color scheme 1. update the background color for side bar, surface and divider in dark mode 2. comment out dandelion and lavender theme temporarily * chore: update top bar BGcolor and icon color * chore: update text color * chore: update share button color on the top bar * chore: add tooltip theme data in global * chore: add hint and tertiary color and update font size pop up menu style * chore: update all the semibold texts color * chore: update all the hover color * chore: update setting BG color * chore: add FlowySvg to get the icon color through current theme 1. Add FlowySvg widget 2. Update all the icons those have hover effect to FlowySvg 3. Recover shader1 for the text color when it is in hovered * chore: update side bar UI 1. Update AddButton hover style 2. Update MenuAppHeader icon color and its hover style 3. Update NewAppButton(New page) font color 4. Update MenuUser username font color * chore: update SettingsDialog style in dart mode 1. Update title and text color 2. Update the hover color on FlowyTextButton 3. Update the LanguageSelectorDropdown background color and hover enter color * chore: update NewAppButton icon in dark mode * chore: update default icon color * chore: rename the hover color and update ViewSectionItem hover color from default theme color * chore: add question bubble background color * chore: update cover image button color * chore: remove fixed icon color on _AddCoverButton * chore: put Dandelion and Lavender color scheme back with basic modification * fix: delete unused import files and deprecated field * chore: add comma and put color back * chore: update AppFlowyPopover background color * chore: remove the hover color on primary and secondary button * chore: update shadow color in dark mode * chore: update SettingsMenuElement hover effect * chore: update the text color in DropdownMenuItem --- .../assets/images/home/new_app_dark.svg | 5 + .../board/presentation/board_page.dart | 4 +- .../presentation/toolbar/board_setting.dart | 2 +- .../calendar/presentation/calendar_day.dart | 2 +- .../widgets/filter/choicechip/choicechip.dart | 2 +- .../widgets/filter/create_filter_list.dart | 2 +- .../widgets/filter/disclosure_button.dart | 2 +- .../widgets/filter/filter_menu.dart | 2 +- .../widgets/footer/grid_footer.dart | 2 +- .../widgets/header/field_cell.dart | 2 +- .../widgets/header/field_type_list.dart | 2 +- .../header/field_type_option_editor.dart | 4 +- .../widgets/header/grid_header.dart | 2 +- .../widgets/header/type_option/date.dart | 4 +- .../widgets/header/type_option/number.dart | 2 +- .../header/type_option/select_option.dart | 4 +- .../type_option/select_option_editor.dart | 2 +- .../grid/presentation/widgets/row/action.dart | 2 +- .../widgets/sort/create_sort_list.dart | 2 +- .../widgets/sort/sort_editor.dart | 6 +- .../presentation/widgets/sort/sort_menu.dart | 2 +- .../widgets/toolbar/grid_group.dart | 2 +- .../widgets/toolbar/grid_property.dart | 4 +- .../widgets/toolbar/grid_setting.dart | 2 +- .../database_view/widgets/card/card.dart | 4 +- .../widgets/row/accessory/cell_accessory.dart | 2 - .../checklist_cell/checklist_cell_editor.dart | 2 +- .../row/cells/date_cell/date_editor.dart | 8 +- .../cells/select_option_cell/extension.dart | 2 +- .../select_option_editor.dart | 2 +- .../widgets/row/cells/url_cell/url_cell.dart | 4 +- .../database_view/widgets/row/row_detail.dart | 2 +- .../plugins/document/presentation/banner.dart | 5 +- .../presentation/more/font_size_switcher.dart | 6 +- .../presentation/more/more_button.dart | 2 +- .../plugins/base/built_in_page_widget.dart | 4 +- .../plugins/base/link_to_page_widget.dart | 2 +- .../plugins/cover/change_cover_popover.dart | 15 ++- .../plugins/cover/cover_node_widget.dart | 14 +-- .../lib/plugins/trash/menu.dart | 10 +- .../lib/plugins/trash/src/trash_cell.dart | 4 +- .../lib/plugins/trash/trash_page.dart | 5 +- .../user/presentation/widgets/background.dart | 1 + .../lib/workspace/application/appearance.dart | 42 ++++--- .../workspace/application/view/view_ext.dart | 2 +- .../presentation/home/home_stack.dart | 2 +- .../home/menu/app/create_button.dart | 9 +- .../home/menu/app/header/add_button.dart | 30 +++-- .../home/menu/app/header/header.dart | 10 +- .../menu/app/header/import/import_panel.dart | 1 + .../home/menu/app/section/item.dart | 15 +-- .../presentation/home/menu/menu.dart | 2 +- .../presentation/home/menu/menu_user.dart | 6 +- .../presentation/home/navigation.dart | 6 +- .../settings/settings_dialog.dart | 5 +- .../widgets/settings_appearance_view.dart | 4 +- .../widgets/settings_language_view.dart | 8 +- .../widgets/settings_menu_element.dart | 48 ++++---- .../widgets/float_bubble/question_bubble.dart | 3 + .../lib/colorscheme/colorscheme.dart | 29 +++++ .../lib/colorscheme/dandelion.dart | 57 +++++++-- .../lib/colorscheme/default_colorscheme.dart | 116 ++++++++++++------ .../flowy_infra/lib/colorscheme/lavender.dart | 54 +++++++- .../packages/flowy_infra/lib/image.dart | 26 +++- .../src/flowy_overlay/appflowy_popover.dart | 2 +- .../lib/src/flowy_overlay/flowy_dialog.dart | 1 + .../lib/style_widget/button.dart | 7 +- .../lib/style_widget/hover.dart | 17 ++- .../widget/buttons/base_styled_button.dart | 10 +- .../lib/widget/buttons/primary_button.dart | 1 - .../lib/widget/buttons/secondary_button.dart | 2 - 71 files changed, 446 insertions(+), 228 deletions(-) create mode 100644 frontend/appflowy_flutter/assets/images/home/new_app_dark.svg diff --git a/frontend/appflowy_flutter/assets/images/home/new_app_dark.svg b/frontend/appflowy_flutter/assets/images/home/new_app_dark.svg new file mode 100644 index 0000000000..f1ab9c8cd3 --- /dev/null +++ b/frontend/appflowy_flutter/assets/images/home/new_app_dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart index cbd85594a0..24c30a0cd3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart @@ -184,7 +184,7 @@ class _BoardContentState extends State { width: 20, child: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), onAddButtonClick: () { @@ -207,7 +207,7 @@ class _BoardContentState extends State { width: 20, child: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), title: FlowyText.medium( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_setting.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_setting.dart index 5e97c9875f..97118c8e32 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_setting.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_setting.dart @@ -113,7 +113,7 @@ class _SettingItem extends StatelessWidget { }, leftIcon: svgWidget( action.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_day.dart b/frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_day.dart index 3a71c2c4ff..0b835f8a39 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_day.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_day.dart @@ -203,7 +203,7 @@ class _NewEventButton extends StatelessWidget { iconPadding: EdgeInsets.zero, icon: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), width: 22, ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/choicechip.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/choicechip.dart index 8aa9892d41..6900f472e0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/choicechip.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/choicechip.dart @@ -43,7 +43,7 @@ class ChoiceChipButton extends StatelessWidget { radius: const BorderRadius.all(Radius.circular(14)), leftIcon: svgWidget( filterInfo.fieldInfo.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), rightIcon: _ChoicechipFilterDesc(filterDesc: filterDesc), hoverColor: AFThemeExtension.of(context).lightGreyHover, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart index e473034fb3..3c8c6b4ba9 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart @@ -162,7 +162,7 @@ class _FilterPropertyCell extends StatelessWidget { onTap: () => onTap(fieldInfo), leftIcon: svgWidget( fieldInfo.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart index 7bab27b745..0f70d7be9d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart @@ -34,7 +34,7 @@ class _DisclosureButtonState extends State { width: 20, icon: svgWidget( "editor/details", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onPressed: () => controller.show(), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_menu.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_menu.dart index a7f17b1e8a..ec8c47dc6f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_menu.dart @@ -76,7 +76,7 @@ class _AddFilterButtonState extends State { hoverColor: AFThemeExtension.of(context).lightGreyHover, leftIcon: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onTap: () => popoverController.show(), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart index 9e4eeb1a4a..44f1d3fb7d 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart @@ -19,7 +19,7 @@ class GridAddRowButton extends StatelessWidget { onTap: () => context.read().add(const GridEvent.createRow()), leftIcon: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart index 91d5368286..3af13c93a2 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart @@ -168,7 +168,7 @@ class FieldCellButton extends StatelessWidget { onTap: onTap, leftIcon: svgWidget( field.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), radius: BorderRadius.zero, text: FlowyText.medium( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart index da6c28d606..f94b46cbee 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart @@ -61,7 +61,7 @@ class FieldTypeCell extends StatelessWidget { onTap: () => onSelectField(fieldType), leftIcon: svgWidget( fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart index 4974490642..5f2364571c 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart @@ -116,11 +116,11 @@ class _SwitchFieldButton extends StatelessWidget { margin: GridSize.typeOptionContentInsets, leftIcon: svgWidget( bloc.state.field.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), rightIcon: svgWidget( "grid/more", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart index 0e4c6e28f5..986a91daa2 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart @@ -182,7 +182,7 @@ class CreateFieldButton extends StatelessWidget { onTap: () {}, leftIcon: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), popupBuilder: (BuildContext popover) { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart index a92ba48fd8..3da0aadd93 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart @@ -153,7 +153,7 @@ class DateFormatButton extends StatelessWidget { onHover: onHover, rightIcon: svgWidget( "grid/more", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); @@ -184,7 +184,7 @@ class TimeFormatButton extends StatelessWidget { onHover: onHover, rightIcon: svgWidget( "grid/more", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/number.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/number.dart index fd5824a0d1..45479046c5 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/number.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/number.dart @@ -61,7 +61,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget { margin: GridSize.typeOptionContentInsets, rightIcon: svgWidget( "grid/more", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), text: Row( children: [ diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart index e30f460c16..cac4f1f0f0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart @@ -194,7 +194,7 @@ class _OptionCellState extends State<_OptionCell> { padding: const EdgeInsets.symmetric(horizontal: 6.0), child: svgWidget( "grid/details", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ], @@ -251,7 +251,7 @@ class _AddOptionButton extends StatelessWidget { }, leftIcon: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart index c248ec01df..b0d6338ae3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart @@ -106,7 +106,7 @@ class _DeleteTag extends StatelessWidget { text: FlowyText.medium(LocaleKeys.grid_selectOption_deleteTag.tr()), leftIcon: svgWidget( "grid/delete", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onTap: () { context diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/action.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/action.dart index 2aecd5f868..9ecaaf055b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/action.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/action.dart @@ -67,7 +67,7 @@ class _ActionCell extends StatelessWidget { }, leftIcon: svgWidget( action.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart index 22f3403a4d..531d781aee 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart @@ -161,7 +161,7 @@ class _SortPropertyCell extends StatelessWidget { onTap: () => onTap(fieldInfo), leftIcon: svgWidget( fieldInfo.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart index 4bd72caa07..8789a6829a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart @@ -125,7 +125,7 @@ class _SortItem extends StatelessWidget { hoverColor: AFThemeExtension.of(context).lightGreyHover, icon: svgWidget( "home/close", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); @@ -187,7 +187,7 @@ class _AddSortButtonState extends State<_AddSortButton> { onTap: () => _popoverController.show(), leftIcon: svgWidget( "home/add", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ), @@ -222,7 +222,7 @@ class _DeleteSortButton extends StatelessWidget { }, leftIcon: svgWidget( "editor/delete", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart index 19b9b1a569..81a07844cf 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart @@ -61,7 +61,7 @@ class SortChoiceChip extends StatelessWidget { final text = LocaleKeys.grid_settings_sort.tr(); final leftIcon = svgWidget( "grid/setting/sort", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ); return SizedBox( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_group.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_group.dart index 127cb63247..c12ac5613a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_group.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_group.dart @@ -82,7 +82,7 @@ class _GridGroupCell extends StatelessWidget { text: FlowyText.medium(fieldInfo.name), leftIcon: svgWidget( fieldInfo.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), rightIcon: rightIcon, onTap: () { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart index 9b65628f4f..0b6f67a6f3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart @@ -96,7 +96,7 @@ class _GridPropertyCellState extends State<_GridPropertyCell> { Widget build(BuildContext context) { final checkmark = svgWidget( widget.fieldInfo.visibility ? 'home/show' : 'home/hide', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ); return SizedBox( @@ -118,7 +118,7 @@ class _GridPropertyCellState extends State<_GridPropertyCell> { text: FlowyText.medium(widget.fieldInfo.name), leftIcon: svgWidget( widget.fieldInfo.fieldType.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), rightIcon: FlowyIconButton( hoverColor: Colors.transparent, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_setting.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_setting.dart index 25b742b6fc..e41d75ce69 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_setting.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_setting.dart @@ -78,7 +78,7 @@ class _SettingItem extends StatelessWidget { onTap: () => onAction(action), leftIcon: svgWidget( action.iconName(), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart index 196b4b40ec..1227ef8e3c 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart @@ -232,7 +232,7 @@ class _CardMoreOption extends StatelessWidget with CardAccessory { padding: const EdgeInsets.all(3.0), child: svgWidget( 'grid/details', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } @@ -254,7 +254,7 @@ class _CardEditOption extends StatelessWidget with CardAccessory { padding: const EdgeInsets.all(3.0), child: svgWidget( 'editor/edit', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart index 39a8bd41f6..031f957ef3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart @@ -7,7 +7,6 @@ import 'package:flowy_infra/size.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:textstyle_extensions/textstyle_extensions.dart'; import '../cell_builder.dart'; @@ -69,7 +68,6 @@ class _PrimaryCellAccessoryState extends State Widget build(BuildContext context) { return Tooltip( message: LocaleKeys.tooltip_openAsPage.tr(), - textStyle: AFThemeExtension.of(context).caption.textColor(Colors.white), child: svgWidget( "grid/expander", color: Theme.of(context).colorScheme.primary, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart index 3e5d744e1e..891e3e7c05 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart @@ -137,7 +137,7 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> { iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2), icon: svgWidget( "editor/details", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart index be6674b5bc..3b338d5355 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart @@ -173,13 +173,13 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> { leftChevronPadding: EdgeInsets.zero, leftChevronIcon: svgWidget( "home/arrow_left", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), rightChevronPadding: EdgeInsets.zero, rightChevronMargin: EdgeInsets.zero, rightChevronIcon: svgWidget( "home/arrow_right", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), headerMargin: const EdgeInsets.only(bottom: 8.0), ), @@ -254,7 +254,7 @@ class _IncludeTimeButton extends StatelessWidget { children: [ svgWidget( "grid/clock", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), const HSpace(4), FlowyText.medium(LocaleKeys.grid_field_includeTime.tr()), @@ -387,7 +387,7 @@ class _DateTypeOptionButton extends StatelessWidget { margin: GridSize.typeOptionContentInsets, rightIcon: svgWidget( "grid/more", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart index c83b8ade30..29b0d9b96e 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart @@ -116,7 +116,7 @@ class SelectOptionTag extends StatelessWidget { hoverColor: Colors.transparent, icon: svgWidget( 'home/close', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ], diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart index 024e88405c..e3ba894373 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart @@ -277,7 +277,7 @@ class _SelectOptionCellState extends State<_SelectOptionCell> { iconPadding: const EdgeInsets.symmetric(horizontal: 6.0), icon: svgWidget( "editor/details", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ], diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart index 9ab086e356..30cd9ae807 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart @@ -213,7 +213,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory> offset: const Offset(0, 8), child: svgWidget( "editor/edit", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), popupBuilder: (BuildContext popoverContext) { return URLEditorPopover( @@ -246,7 +246,7 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory> Widget build(BuildContext context) { return svgWidget( "editor/copy", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart index b92f993382..8579a7e17a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart @@ -103,7 +103,7 @@ class _CloseButton extends StatelessWidget { iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2), icon: svgWidget( "home/close", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/banner.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/banner.dart index 0e85ff85db..8cd8e4decc 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/banner.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/banner.dart @@ -34,7 +34,8 @@ class DocumentBanner extends StatelessWidget { contentPadding: EdgeInsets.zero, bgColor: Colors.transparent, hoverColor: Theme.of(context).colorScheme.primary, - downColor: Theme.of(context).colorScheme.primaryContainer, + highlightColor: + Theme.of(context).colorScheme.primaryContainer, outlineColor: Colors.white, borderRadius: Corners.s8Border, onPressed: onRestore, @@ -50,7 +51,7 @@ class DocumentBanner extends StatelessWidget { contentPadding: EdgeInsets.zero, bgColor: Colors.transparent, hoverColor: Theme.of(context).colorScheme.primaryContainer, - downColor: Theme.of(context).colorScheme.primary, + highlightColor: Theme.of(context).colorScheme.primary, outlineColor: Colors.white, borderRadius: Corners.s8Border, onPressed: onDelete, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/more/font_size_switcher.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/more/font_size_switcher.dart index 37d6434e4d..13814ac186 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/more/font_size_switcher.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/more/font_size_switcher.dart @@ -32,6 +32,7 @@ class _FontSizeSwitcherState extends State { FlowyText.semibold( LocaleKeys.moreAction_fontSize.tr(), fontSize: 12, + color: Theme.of(context).colorScheme.tertiary, ), const SizedBox( height: 5, @@ -43,9 +44,8 @@ class _FontSizeSwitcherState extends State { _updateSelectedFontSize(_fontSizes[index].item2); }, borderRadius: const BorderRadius.all(Radius.circular(5)), - selectedBorderColor: Theme.of(context).colorScheme.primaryContainer, - selectedColor: Theme.of(context).colorScheme.onSurface, - fillColor: Theme.of(context).colorScheme.primaryContainer, + selectedColor: Theme.of(context).colorScheme.tertiary, + fillColor: Theme.of(context).colorScheme.primary, color: Theme.of(context).hintColor, constraints: const BoxConstraints( minHeight: 40.0, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/more/more_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/more/more_button.dart index 5ed6219319..23c5895d52 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/more/more_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/more/more_button.dart @@ -28,7 +28,7 @@ class DocumentMoreButton extends StatelessWidget { child: svgWidget( 'editor/details', size: const Size(18, 18), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/built_in_page_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/built_in_page_widget.dart index 3aa729d051..0df834c355 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/built_in_page_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/built_in_page_widget.dart @@ -119,7 +119,7 @@ class _BuiltInPageWidgetState extends State { iconPadding: const EdgeInsets.all(3), icon: svgWidget( 'common/information', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), // Name @@ -143,7 +143,7 @@ class _BuiltInPageWidgetState extends State { iconPadding: const EdgeInsets.all(3), icon: svgWidget( 'common/settings', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onPressed: () => controller.show(), ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/link_to_page_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/link_to_page_widget.dart index 73cfbeecb4..46c8298b49 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/link_to_page_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/link_to_page_widget.dart @@ -149,7 +149,7 @@ class _LinkToPageMenuState extends State { FlowyButton( leftIcon: svgWidget( _iconName(value), - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), text: FlowyText.regular(value.name), onTap: () => widget.onSelected(app.value1, value), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart index 9aa338b0bc..b339a1b3f0 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart @@ -104,15 +104,24 @@ class _ChangeCoverPopoverState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - FlowyText.semibold(LocaleKeys.document_plugins_cover_colors.tr()), + FlowyText.semibold( + LocaleKeys.document_plugins_cover_colors.tr(), + color: Theme.of(context).colorScheme.tertiary, + ), const SizedBox(height: 10), _buildColorPickerList(), const SizedBox(height: 10), - FlowyText.semibold(LocaleKeys.document_plugins_cover_images.tr()), + FlowyText.semibold( + LocaleKeys.document_plugins_cover_images.tr(), + color: Theme.of(context).colorScheme.tertiary, + ), const SizedBox(height: 10), _buildFileImagePicker(), const SizedBox(height: 10), - FlowyText.semibold(LocaleKeys.document_plugins_cover_abstract.tr()), + FlowyText.semibold( + LocaleKeys.document_plugins_cover_abstract.tr(), + color: Theme.of(context).colorScheme.tertiary, + ), const SizedBox(height: 10), _buildAbstractImagePicker(), ], diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart index 808962e019..8a7ac2807a 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart @@ -156,10 +156,7 @@ class _AddCoverButtonState extends State<_AddCoverButton> { leftIconSize: const Size.square(18), onTap: widget.onTap, useIntrinsicWidth: true, - leftIcon: svgWidget( - 'editor/image', - color: Theme.of(context).colorScheme.onSurface, - ), + leftIcon: const FlowySvg(name: 'editor/image'), text: FlowyText.regular( LocaleKeys.document_plugins_cover_addCover.tr(), ), @@ -174,7 +171,7 @@ class _AddCoverButtonState extends State<_AddCoverButton> { useIntrinsicWidth: true, leftIcon: Icon( Icons.emoji_emotions_outlined, - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, size: 18, ), text: FlowyText.regular(LocaleKeys @@ -197,8 +194,7 @@ class _AddCoverButtonState extends State<_AddCoverButton> { child: FlowyButton( leftIconSize: const Size.square(18), useIntrinsicWidth: true, - leftIcon: Icon(Icons.emoji_emotions_outlined, - color: Theme.of(context).colorScheme.onSurface, + leftIcon: const Icon(Icons.emoji_emotions_outlined, size: 18), text: FlowyText.regular( LocaleKeys.document_plugins_cover_addIcon.tr()), @@ -400,7 +396,7 @@ class _CoverImageState extends State<_CoverImage> { popoverController.show(); }, hoverColor: Theme.of(context).colorScheme.surface, - textColor: Theme.of(context).colorScheme.onSurface, + textColor: Theme.of(context).colorScheme.tertiary, fillColor: Theme.of(context).colorScheme.surface.withOpacity(0.8), width: 120, height: 28, @@ -422,7 +418,7 @@ class _CoverImageState extends State<_CoverImage> { width: 28, icon: svgWidget( 'editor/delete', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).colorScheme.tertiary, ), onPressed: () { widget.onCoverChanged(CoverSelectionType.initial, null); diff --git a/frontend/appflowy_flutter/lib/plugins/trash/menu.dart b/frontend/appflowy_flutter/lib/plugins/trash/menu.dart index f3b73e5404..8708aa7563 100644 --- a/frontend/appflowy_flutter/lib/plugins/trash/menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/trash/menu.dart @@ -45,13 +45,9 @@ class MenuTrash extends StatelessWidget { Widget _render(BuildContext context) { return Row( children: [ - SizedBox( - width: 16, - height: 16, - child: svgWidget( - "home/trash", - color: Theme.of(context).colorScheme.onSurface, - ), + const FlowySvg( + size: Size(16, 16), + name: 'home/trash', ), const HSpace(6), FlowyText.medium(LocaleKeys.trash_text.tr()), diff --git a/frontend/appflowy_flutter/lib/plugins/trash/src/trash_cell.dart b/frontend/appflowy_flutter/lib/plugins/trash/src/trash_cell.dart index 058f6f842a..a46cca2a3b 100644 --- a/frontend/appflowy_flutter/lib/plugins/trash/src/trash_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/trash/src/trash_cell.dart @@ -43,7 +43,7 @@ class TrashCell extends StatelessWidget { iconPadding: const EdgeInsets.all(5), icon: svgWidget( "editor/restore", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), const HSpace(20), @@ -53,7 +53,7 @@ class TrashCell extends StatelessWidget { iconPadding: const EdgeInsets.all(5), icon: svgWidget( "editor/delete", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ], diff --git a/frontend/appflowy_flutter/lib/plugins/trash/trash_page.dart b/frontend/appflowy_flutter/lib/plugins/trash/trash_page.dart index a0947a2de3..28460e9c59 100644 --- a/frontend/appflowy_flutter/lib/plugins/trash/trash_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/trash/trash_page.dart @@ -90,6 +90,7 @@ class _TrashPageState extends State { FlowyText.semibold( LocaleKeys.trash_text.tr(), fontSize: FontSizes.s16, + color: Theme.of(context).colorScheme.tertiary, ), const Spacer(), IntrinsicWidth( @@ -97,7 +98,7 @@ class _TrashPageState extends State { text: FlowyText.medium(LocaleKeys.trash_restoreAll.tr()), leftIcon: svgWidget( 'editor/restore', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onTap: () => context.read().add( const TrashEvent.restoreAll(), @@ -110,7 +111,7 @@ class _TrashPageState extends State { text: FlowyText.medium(LocaleKeys.trash_deleteAll.tr()), leftIcon: svgWidget( 'editor/delete', - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onTap: () => context.read().add(const TrashEvent.deleteAll()), diff --git a/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart b/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart index fe3bdd2a15..f7353ebd5a 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/widgets/background.dart @@ -49,6 +49,7 @@ class FlowyLogoTitle extends StatelessWidget { FlowyText.semibold( title, fontSize: FontSizes.s24, + color: Theme.of(context).colorScheme.tertiary, ), ], ), diff --git a/frontend/appflowy_flutter/lib/workspace/application/appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/appearance.dart index 9cadf7f5ce..b340b8612e 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/appearance.dart @@ -212,14 +212,20 @@ class AppearanceSettingsState with _$AppearanceSettingsState { return ThemeData( brightness: brightness, - textTheme: - _getTextTheme(fontFamily: fontFamily, fontColor: theme.shader1), + textTheme: _getTextTheme(fontFamily: fontFamily, fontColor: theme.text), textSelectionTheme: TextSelectionThemeData( cursorColor: theme.main2, selectionHandleColor: theme.main2, ), - primaryIconTheme: IconThemeData(color: theme.hover), - iconTheme: IconThemeData(color: theme.shader1), + iconTheme: IconThemeData(color: theme.icon), + tooltipTheme: TooltipThemeData( + textStyle: _getFontStyle( + fontFamily: fontFamily, + fontSize: FontSizes.s11, + fontWeight: FontWeight.w400, + fontColor: theme.surface, + ), + ), scrollbarTheme: ScrollbarThemeData( thumbColor: MaterialStateProperty.all(theme.shader3), thickness: MaterialStateProperty.resolveWith((states) { @@ -239,30 +245,38 @@ class AppearanceSettingsState with _$AppearanceSettingsState { ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, canvasColor: theme.shader6, - dividerColor: theme.shader6, - hintColor: theme.shader3, + dividerColor: theme.divider, + hintColor: theme.hint, + //action item hover color + hoverColor: theme.hoverBG2, disabledColor: theme.shader4, highlightColor: theme.main1, indicatorColor: theme.main1, toggleableActiveColor: theme.main1, + cardColor: theme.input, colorScheme: ColorScheme( brightness: brightness, - primary: theme.main1, - onPrimary: _white, + primary: theme.primary, + onPrimary: theme.onPrimary, primaryContainer: theme.main2, onPrimaryContainer: _white, - secondary: theme.hover, + // page title hover color + secondary: theme.hoverBG1, onSecondary: theme.shader1, + // setting value hover color secondaryContainer: theme.selector, - onSecondaryContainer: theme.shader1, + onSecondaryContainer: theme.topbarBg, + tertiary: theme.shader7, + tertiaryContainer: theme.questionBubbleBG, background: theme.surface, - onBackground: theme.shader1, + onBackground: theme.text, surface: theme.surface, - onSurface: theme.shader1, + // text&icon color when it is hovered + onSurface: theme.hoverFG, onError: theme.shader7, error: theme.red, outline: theme.shader4, - surfaceVariant: theme.bg1, + surfaceVariant: theme.sidebarBg, shadow: theme.shadow, ), extensions: [ @@ -278,7 +292,7 @@ class AppearanceSettingsState with _$AppearanceSettingsState { tint7: theme.tint7, tint8: theme.tint8, tint9: theme.tint9, - greyHover: theme.bg2, + greyHover: theme.hoverBG1, greySelect: theme.bg3, lightGreyHover: theme.shader6, toggleOffFill: theme.shader5, diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart index c666d77b2d..200ce23686 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart @@ -36,7 +36,7 @@ extension ViewExtension on ViewPB { Widget renderThumbnail({Color? iconColor}) { String thumbnail = "file_icon"; - final Widget widget = svgWidget(thumbnail, color: iconColor); + final Widget widget = FlowySvg(name: thumbnail); return widget; } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart index 6a5bc0e1d4..e73577d818 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart @@ -199,7 +199,7 @@ class HomeTopBar extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - color: Theme.of(context).colorScheme.surface, + color: Theme.of(context).colorScheme.onSecondaryContainer, height: HomeSizes.topBarHeight, child: Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/create_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/create_button.dart index d5584173bd..be6fddf37a 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/create_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/create_button.dart @@ -18,9 +18,14 @@ class NewAppButton extends StatelessWidget { LocaleKeys.newPageText.tr(), fillColor: Colors.transparent, hoverColor: Colors.transparent, - fontColor: Theme.of(context).colorScheme.onSurfaceVariant, + fontColor: Theme.of(context).colorScheme.tertiary, onPressed: () async => await _showCreateAppDialog(context), - heading: svgWidget("home/new_app", size: const Size(16, 16)), + heading: Theme.of(context).brightness == Brightness.light + ? svgWidget("home/new_app", size: const Size(16, 16)) + : svgWidget( + "home/new_app_dark", + size: const Size(16, 16), + ), padding: EdgeInsets.symmetric(horizontal: Insets.l, vertical: 20), ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart index 351012bc38..2b55fd736d 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart @@ -6,9 +6,9 @@ import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy_editor/appflowy_editor.dart' show Document; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/image.dart'; -import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; -import 'package:styled_widget/styled_widget.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -50,13 +50,19 @@ class AddButton extends StatelessWidget { actions: actions, offset: const Offset(0, 8), buildChild: (controller) { - return FlowyIconButton( + return SizedBox( width: 22, - onPressed: () => controller.show(), - icon: svgWidget( - "home/add", - color: Theme.of(context).colorScheme.onSurface, - ).padding(horizontal: 3, vertical: 3), + child: InkWell( + onTap: () => controller.show(), + child: FlowyHover( + style: HoverStyle( + hoverColor: AFThemeExtension.of(context).greySelect, + ), + builder: (context, onHover) => const FlowySvg( + name: 'home/add', + ), + ), + ), ); }, onSelected: (action, controller) { @@ -83,8 +89,7 @@ class AddButtonActionWrapper extends ActionCell { AddButtonActionWrapper({required this.pluginBuilder}); @override - Widget? leftIcon(Color iconColor) => - svgWidget(pluginBuilder.menuIcon, color: iconColor); + Widget? leftIcon(Color iconColor) => FlowySvg(name: pluginBuilder.menuIcon); @override String get name => pluginBuilder.menuName; @@ -100,9 +105,8 @@ class ImportActionWrapper extends ActionCell { }); @override - Widget? leftIcon(Color iconColor) => svgWidget( - 'editor/import', - color: iconColor, + Widget? leftIcon(Color iconColor) => const FlowySvg( + name: 'editor/import', ); @override diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart index e94f7380ea..c2161066c9 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart @@ -5,7 +5,6 @@ import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:expandable/expandable.dart'; -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/icon_data.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart'; @@ -15,7 +14,6 @@ import 'package:appflowy/workspace/application/app/app_bloc.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:flowy_infra/image.dart'; -import 'package:textstyle_extensions/textstyle_extensions.dart'; import '../menu_app.dart'; import 'add_button.dart'; @@ -58,7 +56,7 @@ class MenuAppHeader extends StatelessWidget { theme: ExpandableThemeData( expandIcon: FlowyIconData.drop_down_show, collapseIcon: FlowyIconData.drop_down_hide, - iconColor: Theme.of(context).colorScheme.onSurface, + iconColor: Theme.of(context).colorScheme.tertiary, iconSize: MenuAppSizes.iconSize, iconPadding: const EdgeInsets.fromLTRB(0, 0, 10, 0), hasIcon: false, @@ -104,7 +102,6 @@ class MenuAppHeader extends StatelessWidget { Widget _renderCreateViewButton(BuildContext context) { return Tooltip( message: LocaleKeys.menuAppHeader_addPageTooltip.tr(), - textStyle: AFThemeExtension.of(context).caption.textColor(Colors.white), child: AddButton( onSelected: (pluginBuilder, document) { context.read().add( @@ -139,9 +136,9 @@ extension AppDisclosureExtension on AppDisclosureAction { Widget icon(Color iconColor) { switch (this) { case AppDisclosureAction.rename: - return svgWidget('editor/edit', color: iconColor); + return const FlowySvg(name: 'editor/edit'); case AppDisclosureAction.delete: - return svgWidget('editor/delete', color: iconColor); + return const FlowySvg(name: 'editor/delete'); } } } @@ -174,6 +171,7 @@ class AppActionList extends StatelessWidget { builder: (context, app) => FlowyText.medium( app.name, overflow: TextOverflow.ellipsis, + color: Theme.of(context).colorScheme.tertiary, ), ), ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart index 0250f443d0..f1996af8f8 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart @@ -24,6 +24,7 @@ Future showImportPanel( title: FlowyText.semibold( LocaleKeys.moreAction_import.tr(), fontSize: 20, + color: Theme.of(context).colorScheme.tertiary, ), content: _ImportPanel(importCallback: callback), contentPadding: const EdgeInsets.symmetric( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/item.dart index f261a946a9..e6d328d797 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/section/item.dart @@ -4,7 +4,6 @@ import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/presentation/home/menu/menu.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; @@ -51,7 +50,7 @@ class ViewSectionItem extends StatelessWidget { onTap: () => onSelected(blocContext.read().state.view), child: FlowyHover( style: HoverStyle( - hoverColor: AFThemeExtension.of(context).greySelect, + hoverColor: Theme.of(context).colorScheme.secondary, ), // If current state.isEditing is true, the hover should not // rebuild when onEnter/onExit events happened. @@ -60,7 +59,6 @@ class ViewSectionItem extends StatelessWidget { blocContext, onHover, state, - Theme.of(context).colorScheme.onSurface, ), isSelected: () => state.isEditing || isSelected, ), @@ -75,13 +73,12 @@ class ViewSectionItem extends StatelessWidget { BuildContext blocContext, bool onHover, ViewState state, - Color iconColor, ) { List children = [ SizedBox( width: 16, height: 16, - child: state.view.renderThumbnail(iconColor: iconColor), + child: state.view.renderThumbnail(), ), const HSpace(2), Expanded( @@ -154,11 +151,11 @@ extension ViewDisclosureExtension on ViewDisclosureAction { Widget icon(Color iconColor) { switch (this) { case ViewDisclosureAction.rename: - return svgWidget('editor/edit', color: iconColor); + return const FlowySvg(name: 'editor/edit'); case ViewDisclosureAction.delete: - return svgWidget('editor/delete', color: iconColor); + return const FlowySvg(name: 'editor/delete'); case ViewDisclosureAction.duplicate: - return svgWidget('editor/copy', color: iconColor); + return const FlowySvg(name: 'editor/copy'); } } } @@ -186,7 +183,7 @@ class ViewDisclosureButton extends StatelessWidget { width: 26, icon: svgWidget( "editor/details", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), onPressed: () { onEdit(true); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu.dart index 92f5f4621b..11cfa05042 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu.dart @@ -229,7 +229,7 @@ class MenuTopBar extends StatelessWidget { iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), icon: svgWidget( "home/hide_menu", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), ), ) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu_user.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu_user.dart index 21cc90a04e..236dd60479 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu_user.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/menu_user.dart @@ -2,7 +2,6 @@ import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart'; import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart'; -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; @@ -13,7 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:textstyle_extensions/textstyle_extensions.dart'; class MenuUser extends StatelessWidget { final UserProfilePB user; @@ -69,6 +67,7 @@ class MenuUser extends StatelessWidget { return FlowyText.medium( name, overflow: TextOverflow.ellipsis, + color: Theme.of(context).colorScheme.tertiary, ); } @@ -76,7 +75,6 @@ class MenuUser extends StatelessWidget { final userProfile = context.read().state.userProfile; return Tooltip( message: LocaleKeys.settings_menu_open.tr(), - textStyle: AFThemeExtension.of(context).caption.textColor(Colors.white), child: IconButton( onPressed: () { showDialog( @@ -90,7 +88,7 @@ class MenuUser extends StatelessWidget { dimension: 20, child: svgWidget( "home/settings", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).colorScheme.tertiary, ), ), ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/navigation.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/navigation.dart index f1818fc162..8fcc3b2f19 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/navigation.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/navigation.dart @@ -6,14 +6,12 @@ import 'package:appflowy/workspace/presentation/home/home_stack.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/size.dart'; -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; -import 'package:textstyle_extensions/textstyle_extensions.dart'; typedef NaviAction = void Function(); @@ -87,7 +85,7 @@ class FlowyNavigation extends StatelessWidget { iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2), icon: svgWidget( "home/hide_menu", - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).iconTheme.color, ), )), ); @@ -180,11 +178,9 @@ TextSpan sidebarTooltipTextSpan(BuildContext context, String hintText) => children: [ TextSpan( text: "$hintText\n", - style: AFThemeExtension.of(context).callout.textColor(Colors.white), ), TextSpan( text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\", - style: AFThemeExtension.of(context).caption.textColor(Colors.white60), ), ], ); 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 d7b3f35a17..09e719c2e4 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart @@ -32,6 +32,7 @@ class SettingsDialog extends StatelessWidget { LocaleKeys.settings_title.tr(), fontSize: 20, fontWeight: FontWeight.w700, + color: Theme.of(context).colorScheme.tertiary, ), ), child: ScaffoldMessenger( @@ -54,7 +55,9 @@ class SettingsDialog extends StatelessWidget { context.read().state.page, ), ), - const VerticalDivider(), + VerticalDivider( + color: Theme.of(context).dividerColor, + ), const SizedBox(width: 10), Expanded( child: getSettingsView( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart index d4e93b3548..81149f9f34 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart @@ -50,8 +50,8 @@ class ThemeSetting extends StatelessWidget { direction: PopoverDirection.bottomWithRightAligned, child: FlowyTextButton( currentTheme, + fontColor: Theme.of(context).colorScheme.onBackground, fillColor: Colors.transparent, - hoverColor: Theme.of(context).colorScheme.secondary, onPressed: () {}, ), popupBuilder: (BuildContext context) { @@ -107,8 +107,8 @@ class ThemeModeSetting extends StatelessWidget { direction: PopoverDirection.bottomWithRightAligned, child: FlowyTextButton( _themeModeLabelText(currentThemeMode), + fontColor: Theme.of(context).colorScheme.onBackground, fillColor: Colors.transparent, - hoverColor: Theme.of(context).colorScheme.secondary, onPressed: () {}, ), popupBuilder: (BuildContext context) { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart index 7b76aa6157..2807554195 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_language_view.dart @@ -47,7 +47,7 @@ class _LanguageSelectorDropdownState extends State { void hoverEnterLanguage() { setState(() { - currHoverColor = Theme.of(context).colorScheme.primary; + currHoverColor = Theme.of(context).colorScheme.secondaryContainer; }); } @@ -67,6 +67,7 @@ class _LanguageSelectorDropdownState extends State { padding: const EdgeInsets.symmetric(horizontal: 6), child: DropdownButton( value: context.locale, + dropdownColor: Theme.of(context).cardColor, onChanged: (locale) { context .read() @@ -80,7 +81,10 @@ class _LanguageSelectorDropdownState extends State { value: locale, child: Padding( padding: const EdgeInsets.all(12.0), - child: FlowyText.medium(languageFromLocale(locale)), + child: FlowyText.medium( + languageFromLocale(locale), + color: Theme.of(context).colorScheme.tertiary, + ), ), ); }).toList(), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu_element.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu_element.dart index 7e6f4e7060..207b03c78f 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu_element.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu_element.dart @@ -1,5 +1,6 @@ import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart'; import 'package:flowy_infra/size.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; @@ -21,29 +22,32 @@ class SettingsMenuElement extends StatelessWidget { @override Widget build(BuildContext context) { - return ListTile( - leading: Icon( - icon, - size: 16, - color: page == selectedPage - ? Theme.of(context).colorScheme.onSurface - : Theme.of(context).colorScheme.onSurface, + return FlowyHover( + style: HoverStyle( + hoverColor: Theme.of(context).colorScheme.primary, ), - onTap: () { - changeSelectedPage(page); - }, - selected: page == selectedPage, - selectedColor: Theme.of(context).colorScheme.onSurface, - selectedTileColor: Theme.of(context).colorScheme.primaryContainer, - hoverColor: Theme.of(context).colorScheme.primary, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5), - ), - minLeadingWidth: 0, - title: FlowyText.semibold( - label, - fontSize: FontSizes.s14, - overflow: TextOverflow.ellipsis, + child: ListTile( + leading: Icon(icon, + size: 16, + color: page == selectedPage + ? Theme.of(context).colorScheme.onSurface + : null), + onTap: () { + changeSelectedPage(page); + }, + selected: page == selectedPage, + selectedColor: Theme.of(context).colorScheme.onSurface, + selectedTileColor: Theme.of(context).colorScheme.primary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + minLeadingWidth: 0, + title: FlowyText.semibold(label, + fontSize: FontSizes.s14, + overflow: TextOverflow.ellipsis, + color: page == selectedPage + ? Theme.of(context).colorScheme.onSurface + : null), ), ); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index c6bb7f3ca0..e6ecf09270 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -48,6 +48,9 @@ class BubbleActionList extends StatelessWidget { '?', tooltip: LocaleKeys.questionBubble_help.tr(), fontWeight: FontWeight.w600, + fontColor: Theme.of(context).colorScheme.tertiary, + fillColor: Theme.of(context).colorScheme.tertiaryContainer, + hoverColor: Theme.of(context).colorScheme.tertiaryContainer, mainAxisAlignment: MainAxisAlignment.center, radius: Corners.s10Border, onPressed: () => controller.show(), diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/colorscheme.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/colorscheme.dart index 76d274f4c8..b109dbad8d 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/colorscheme.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/colorscheme.dart @@ -56,6 +56,22 @@ abstract class FlowyColorScheme { final Color main1; final Color main2; final Color shadow; + final Color sidebarBg; + final Color divider; + final Color topbarBg; + final Color icon; + final Color text; + final Color input; + final Color hint; + final Color primary; + final Color onPrimary; + //page title hover effect + final Color hoverBG1; + //action item hover effect + final Color hoverBG2; + //the text color when it is hovered + final Color hoverFG; + final Color questionBubbleBG; const FlowyColorScheme({ required this.surface, @@ -87,6 +103,19 @@ abstract class FlowyColorScheme { required this.main1, required this.main2, required this.shadow, + required this.sidebarBg, + required this.divider, + required this.topbarBg, + required this.icon, + required this.text, + required this.input, + required this.hint, + required this.primary, + required this.onPrimary, + required this.hoverBG1, + required this.hoverBG2, + required this.hoverFG, + required this.questionBubbleBG, }); factory FlowyColorScheme.builtIn(String themeName, Brightness brightness) { diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/dandelion.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/dandelion.dart index 21492f43b4..00655eef21 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/dandelion.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/dandelion.dart @@ -4,6 +4,21 @@ import 'colorscheme.dart'; const _black = Color(0xff000000); const _white = Color(0xFFFFFFFF); +const _lightHover = Color(0xFFe0f8ff); +const _lightSelector = Color(0xfff2fcff); +const _lightBg1 = Color(0xFFFFD13E); +const _lightBg2 = Color(0xffedeef2); +const _lightShader1 = Color(0xff333333); +const _lightShader3 = Color(0xff828282); +const _lightShader6 = Color(0xfff2f2f2); +const _lightMain1 = Color(0xffe21f74); + +const _darkShader1 = Color(0xff131720); +const _darkShader2 = Color(0xff1A202C); +const _darkShader3 = Color(0xff363D49); +const _darkShader5 = Color(0xffBBC3CD); +const _darkShader6 = Color(0xffF2F2F2); +const _darkMain1 = Color(0xffe21f74); class DandelionColorScheme extends FlowyColorScheme { const DandelionColorScheme.light() @@ -20,8 +35,8 @@ class DandelionColorScheme extends FlowyColorScheme { shader4: const Color(0xffbdbdbd), shader5: const Color(0xffe0e0e0), shader6: const Color(0xfff2f2f2), - shader7: const Color(0xffffffff), - bg1: const Color(0xFFFFD13E), + shader7: _black, + bg1: _lightBg1, bg2: const Color(0xffedeef2), bg3: const Color(0xffe2e4eb), bg4: const Color(0xff2c144b), @@ -34,9 +49,22 @@ class DandelionColorScheme extends FlowyColorScheme { tint7: const Color(0xffddffd6), tint8: const Color(0xffdefff1), tint9: const Color(0xffe1fbff), - main1: const Color(0xffe21f74), + main1: _lightMain1, main2: const Color.fromARGB(255, 224, 25, 111), shadow: _black, + sidebarBg: _lightBg1, + divider: _lightShader6, + topbarBg: _white, + icon: _lightShader1, + text: _lightShader1, + input: _white, + hint: _lightShader3, + primary: _lightMain1, + onPrimary: _white, + hoverBG1: _lightBg2, + hoverBG2: _lightHover, + hoverFG: _lightShader1, + questionBubbleBG: _lightSelector, ); const DandelionColorScheme.dark() @@ -48,12 +76,12 @@ class DandelionColorScheme extends FlowyColorScheme { yellow: const Color(0xffffd667), green: const Color(0xff66cf80), shader1: _white, - shader2: const Color(0xffffffff), + shader2: _darkShader2, shader3: const Color(0xff828282), shader4: const Color(0xffbdbdbd), - shader5: _white, - shader6: _black, - shader7: _black, + shader5: _darkShader5, + shader6: _darkShader6, + shader7: _white, bg1: const Color(0xFFD5A200), bg2: _black, bg3: const Color(0xff4f4f4f), @@ -67,8 +95,21 @@ class DandelionColorScheme extends FlowyColorScheme { tint7: const Color(0xffbcffad), tint8: const Color(0xffadffe2), tint9: const Color(0xffade4ff), - main1: const Color(0xffe21f74), + main1: _darkMain1, main2: const Color.fromARGB(255, 224, 25, 111), shadow: _black, + sidebarBg: const Color(0xff232B38), + divider: _darkShader3, + topbarBg: _darkShader1, + icon: _darkShader5, + text: _darkShader5, + input: const Color(0xff282E3A), + hint: _darkShader5, + primary: _darkMain1, + onPrimary: _darkShader1, + hoverBG1: _darkMain1, + hoverBG2: _darkMain1, + hoverFG: _darkShader1, + questionBubbleBG: _darkShader3, ); } diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/default_colorscheme.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/default_colorscheme.dart index accd81a07e..590b5c7bc6 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/default_colorscheme.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/default_colorscheme.dart @@ -4,25 +4,39 @@ import 'colorscheme.dart'; const _black = Color(0xff000000); const _white = Color(0xFFFFFFFF); +const _lightHover = Color(0xFFe0f8ff); +const _lightSelector = Color(0xfff2fcff); +const _lightBg1 = Color(0xfff7f8fc); +const _lightBg2 = Color(0xffedeef2); +const _lightShader1 = Color(0xff333333); +const _lightShader3 = Color(0xff828282); +const _lightShader6 = Color(0xfff2f2f2); +const _lightMain1 = Color(0xff00bcf0); +const _darkShader1 = Color(0xff131720); +const _darkShader2 = Color(0xff1A202C); +const _darkShader3 = Color(0xff363D49); +const _darkShader5 = Color(0xffBBC3CD); +const _darkShader6 = Color(0xffF2F2F2); +const _darkMain1 = Color(0xff00BCF0); class DefaultColorScheme extends FlowyColorScheme { const DefaultColorScheme.light() : super( - surface: Colors.white, - hover: const Color(0xFFe0f8ff), - selector: const Color(0xfff2fcff), + surface: _white, + hover: _lightHover, + selector: _lightSelector, red: const Color(0xfffb006d), yellow: const Color(0xffffd667), green: const Color(0xff66cf80), - shader1: const Color(0xff333333), + shader1: _lightShader1, shader2: const Color(0xff4f4f4f), - shader3: const Color(0xff828282), + shader3: _lightShader3, shader4: const Color(0xffbdbdbd), shader5: const Color(0xffe0e0e0), - shader6: const Color(0xfff2f2f2), - shader7: const Color(0xffffffff), - bg1: const Color(0xfff7f8fc), - bg2: const Color(0xffedeef2), + shader6: _lightShader6, + shader7: _lightShader1, + bg1: _lightBg1, + bg2: _lightBg2, bg3: const Color(0xffe2e4eb), bg4: const Color(0xff2c144b), tint1: const Color(0xffe8e0ff), @@ -34,41 +48,67 @@ class DefaultColorScheme extends FlowyColorScheme { tint7: const Color(0xffddffd6), tint8: const Color(0xffdefff1), tint9: const Color(0xffe1fbff), - main1: const Color(0xff00bcf0), + main1: _lightMain1, main2: const Color(0xff00b7ea), shadow: _black, + sidebarBg: _lightBg1, + divider: _lightShader6, + topbarBg: _white, + icon: _lightShader1, + text: _lightShader1, + input: _white, + hint: _lightShader3, + primary: _lightMain1, + onPrimary: _white, + hoverBG1: _lightBg2, + hoverBG2: _lightHover, + hoverFG: _lightShader1, + questionBubbleBG: _lightSelector, ); const DefaultColorScheme.dark() : super( - surface: const Color(0xff292929), - hover: const Color(0xff1f1f1f), - selector: const Color(0xff333333), + surface: _darkShader2, + hover: _darkMain1, + selector: _darkShader2, red: const Color(0xfffb006d), - yellow: const Color(0xffffd667), - green: const Color(0xff66cf80), - shader1: _white, - shader2: const Color(0xffffffff), - shader3: const Color(0xff828282), - shader4: const Color(0xffbdbdbd), - shader5: _white, - shader6: _black, - shader7: _black, - bg1: _black, - bg2: _black, - bg3: const Color(0xff4f4f4f), - bg4: const Color(0xff2c144b), - tint1: const Color(0xffc3adff), - tint2: const Color(0xffffadf9), - tint3: const Color(0xffffadad), - tint4: const Color(0xffffcfad), - tint5: const Color(0xfffffead), - tint6: const Color(0xffe6ffa3), - tint7: const Color(0xffbcffad), - tint8: const Color(0xffadffe2), - tint9: const Color(0xffade4ff), - main1: const Color(0xff00bcf0), - main2: const Color(0xff009cc7), - shadow: _black, + yellow: const Color(0xffF7CF46), + green: const Color(0xff66CF80), + shader1: _darkShader1, + shader2: _darkShader2, + shader3: _darkShader3, + shader4: const Color(0xff7C8CA5), + shader5: _darkShader5, + shader6: _darkShader6, + shader7: _white, + bg1: const Color(0xffF7F8FC), + bg2: const Color(0xffEDEEF2), + bg3: _darkMain1, + bg4: const Color(0xff2C144B), + tint1: const Color(0xff8738F5), + tint2: const Color(0xffE6336E), + tint3: const Color(0xffFF2D9E), + tint4: const Color(0xffE9973E), + tint5: const Color(0xffFBF000), + tint6: const Color(0xffC0F000), + tint7: const Color(0xff15F74E), + tint8: const Color(0xff00F0E2), + tint9: const Color(0xff00BCF0), + main1: _darkMain1, + main2: const Color(0xff00B7EA), + shadow: const Color(0xff0F131C), + sidebarBg: const Color(0xff232B38), + divider: _darkShader3, + topbarBg: _darkShader1, + icon: _darkShader5, + text: _darkShader5, + input: const Color(0xff282E3A), + hint: _darkShader5, + primary: _darkMain1, + onPrimary: _darkShader1, + hoverBG1: _darkMain1, + hoverBG2: _darkMain1, + hoverFG: _darkShader1, + questionBubbleBG: _darkShader3, ); } diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/lavender.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/lavender.dart index 7012077250..83f0557349 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/lavender.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/colorscheme/lavender.dart @@ -5,6 +5,22 @@ import 'colorscheme.dart'; const _black = Color(0xff000000); const _white = Color(0xFFFFFFFF); +const _lightHover = Color(0xFFe0f8ff); +const _lightSelector = Color(0xfff2fcff); +const _lightBg1 = Color(0xfff7f8fc); +const _lightBg2 = Color(0xffedeef2); +const _lightShader1 = Color(0xff333333); +const _lightShader3 = Color(0xff828282); +const _lightShader6 = Color(0xfff2f2f2); +const _lightMain1 = Color(0xffA652FB); + +const _darkShader1 = Color(0xff131720); +const _darkShader2 = Color(0xff1A202C); +const _darkShader3 = Color(0xff363D49); +const _darkShader5 = Color(0xffBBC3CD); +const _darkShader6 = Color(0xffF2F2F2); +const _darkMain1 = Color(0xffA652FB); + class LavenderColorScheme extends FlowyColorScheme { const LavenderColorScheme.light() : super( @@ -20,7 +36,7 @@ class LavenderColorScheme extends FlowyColorScheme { shader4: const Color(0xffbdbdbd), shader5: const Color(0xffe0e0e0), shader6: const Color(0xfff2f2f2), - shader7: const Color(0xffffffff), + shader7: _black, bg1: const Color(0xffAC59FF), bg2: const Color(0xffedeef2), bg3: const Color(0xffe2e4eb), @@ -34,9 +50,22 @@ class LavenderColorScheme extends FlowyColorScheme { tint7: const Color(0xffddffd6), tint8: const Color(0xffdefff1), tint9: const Color(0xffe1fbff), - main1: const Color(0xffA652FB), + main1: _lightMain1, main2: const Color(0xff9327FF), shadow: _black, + sidebarBg: _lightBg1, + divider: _lightShader6, + topbarBg: _white, + icon: _lightShader1, + text: _lightShader1, + input: _white, + hint: _lightShader3, + primary: _lightMain1, + onPrimary: _white, + hoverBG1: _lightBg2, + hoverBG2: _lightHover, + hoverFG: _lightShader1, + questionBubbleBG: _lightSelector, ); const LavenderColorScheme.dark() @@ -48,12 +77,12 @@ class LavenderColorScheme extends FlowyColorScheme { yellow: const Color(0xffffd667), green: const Color(0xff66cf80), shader1: _white, - shader2: const Color(0xffffffff), + shader2: _darkShader2, shader3: const Color(0xff828282), shader4: const Color(0xffbdbdbd), shader5: _white, - shader6: _black, - shader7: _black, + shader6: _darkShader6, + shader7: _white, bg1: const Color(0xff8C23F6), bg2: _black, bg3: const Color(0xff4f4f4f), @@ -67,8 +96,21 @@ class LavenderColorScheme extends FlowyColorScheme { tint7: const Color(0xffbcffad), tint8: const Color(0xffadffe2), tint9: const Color(0xffade4ff), - main1: const Color(0xffA652FB), + main1: _darkMain1, main2: const Color(0xff9327FF), shadow: _black, + sidebarBg: const Color(0xff232B38), + divider: _darkShader3, + topbarBg: _darkShader1, + icon: _darkShader5, + text: _darkShader5, + input: const Color(0xff282E3A), + hint: _darkShader5, + primary: _darkMain1, + onPrimary: _darkShader1, + hoverBG1: _darkMain1, + hoverBG2: _darkMain1, + hoverFG: _darkShader1, + questionBubbleBG: _darkShader3, ); } diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart index 4ed4da10bf..577e3c182a 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/image.dart @@ -1,6 +1,30 @@ -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter/material.dart'; + +/// For icon that needs to change color when it is on hovered +/// +/// Get the hover color from ThemeData +class FlowySvg extends StatelessWidget { + const FlowySvg({super.key, this.size, required this.name}); + final String name; + final Size? size; + + @override + Widget build(BuildContext context) { + if (size != null) { + return SizedBox.fromSize( + size: size, + child: SvgPicture.asset('assets/images/$name.svg', + color: Theme.of(context).iconTheme.color), + ); + } else { + return SvgPicture.asset('assets/images/$name.svg', + color: Theme.of(context).iconTheme.color); + } + } +} + Widget svgWidget(String name, {Size? size, Color? color}) { if (size != null) { return SizedBox.fromSize( diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_popover.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_popover.dart index 3673720220..1fcc19fb11 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_popover.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_popover.dart @@ -81,7 +81,7 @@ class _PopoverContainer extends StatelessWidget { Widget build(BuildContext context) { final decoration = this.decoration ?? FlowyDecoration.decoration( - Theme.of(context).colorScheme.surface, + Theme.of(context).cardColor, Theme.of(context).colorScheme.shadow.withOpacity(0.15), ); diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart index f8cc85938b..033f5d2a5d 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart @@ -26,6 +26,7 @@ class FlowyDialog extends StatelessWidget { final size = windowSize * 0.7; return SimpleDialog( contentPadding: EdgeInsets.zero, + backgroundColor: Theme.of(context).cardColor, title: title, shape: shape ?? RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart index 3c958a160a..22b1ff81e8 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart @@ -1,11 +1,9 @@ -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/ignore_parent_gesture.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; -import 'package:textstyle_extensions/textstyle_extensions.dart'; class FlowyButton extends StatelessWidget { final Widget text; @@ -177,7 +175,8 @@ class FlowyTextButton extends StatelessWidget { highlightElevation: 0, shape: RoundedRectangleBorder(borderRadius: radius ?? Corners.s6Border), fillColor: fillColor ?? Theme.of(context).colorScheme.secondaryContainer, - hoverColor: hoverColor ?? Theme.of(context).colorScheme.secondary, + hoverColor: + hoverColor ?? Theme.of(context).colorScheme.secondaryContainer, focusColor: Colors.transparent, splashColor: Colors.transparent, highlightColor: Colors.transparent, @@ -195,7 +194,6 @@ class FlowyTextButton extends StatelessWidget { if (tooltip != null) { child = Tooltip( message: tooltip!, - textStyle: AFThemeExtension.of(context).caption.textColor(Colors.white), child: child, ); } @@ -285,7 +283,6 @@ class FlowyRichTextButton extends StatelessWidget { if (tooltip != null) { child = Tooltip( message: tooltip!, - textStyle: AFThemeExtension.of(context).caption.textColor(Colors.white), child: child, ); } diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart index 8d8b64dcf3..bf9d255e2f 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart @@ -137,7 +137,22 @@ class FlowyHoverContainer extends StatelessWidget { color: style.hoverColor ?? Theme.of(context).colorScheme.secondary, borderRadius: style.borderRadius, ), - child: child, + child: + //override text's theme with new color when it is hovered + Theme( + data: Theme.of(context).copyWith( + textTheme: Theme.of(context).textTheme.copyWith( + bodyMedium: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), + ), + iconTheme: Theme.of(context) + .iconTheme + .copyWith(color: Theme.of(context).colorScheme.onSurface), + ), + child: child!, + ), ); } } diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/base_styled_button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/base_styled_button.dart index 4aefb7dd55..b9c5894b3a 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/base_styled_button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/base_styled_button.dart @@ -9,7 +9,7 @@ class BaseStyledButton extends StatefulWidget { final Color? bgColor; final Color? focusColor; final Color? hoverColor; - final Color? downColor; + final Color? highlightColor; final EdgeInsets? contentPadding; final double? minWidth; final double? minHeight; @@ -34,7 +34,7 @@ class BaseStyledButton extends StatefulWidget { this.minHeight, this.borderRadius, this.hoverColor, - this.downColor, + this.highlightColor, this.shape, this.useBtnText = true, this.autoFocus = false, @@ -116,10 +116,8 @@ class BaseStyledBtnState extends State { highlightElevation: 0, focusElevation: 0, fillColor: Colors.transparent, - hoverColor: - widget.hoverColor ?? Theme.of(context).colorScheme.secondary, - highlightColor: - widget.downColor ?? Theme.of(context).colorScheme.primary, + hoverColor: widget.hoverColor ?? Colors.transparent, + highlightColor: widget.highlightColor ?? Colors.transparent, focusColor: widget.focusColor ?? Colors.grey.withOpacity(0.35), constraints: BoxConstraints( minHeight: widget.minHeight ?? 0, minWidth: widget.minWidth ?? 0), diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart index 1f7491f133..e67a7a15df 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart @@ -42,7 +42,6 @@ class PrimaryButton extends StatelessWidget { contentPadding: EdgeInsets.zero, bgColor: Theme.of(context).colorScheme.primary, hoverColor: Theme.of(context).colorScheme.primaryContainer, - downColor: Theme.of(context).colorScheme.primary, borderRadius: bigMode ? Corners.s12Border : Corners.s8Border, onPressed: onPressed, child: child, diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart index 8caa135463..eae3c58a2a 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart @@ -42,8 +42,6 @@ class SecondaryButton extends StatelessWidget { minHeight: bigMode ? 40 : 38, contentPadding: EdgeInsets.zero, bgColor: Theme.of(context).colorScheme.surface, - hoverColor: Theme.of(context).colorScheme.secondary, - downColor: Theme.of(context).colorScheme.primary, outlineColor: Theme.of(context).colorScheme.primary, borderRadius: bigMode ? Corners.s12Border : Corners.s8Border, onPressed: onPressed, From 7f11584b5806d721cc2beea676ccedffe9e834e7 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 30 Mar 2023 10:09:15 +0800 Subject: [PATCH 18/37] chore: sync 0.1.2 (#2135) --- .github/workflows/release.yml | 10 ++++++++++ CHANGELOG.md | 7 +++++++ frontend/Makefile.toml | 2 +- frontend/appflowy_flutter/pubspec.yaml | 3 +-- frontend/scripts/makefile/flutter.toml | 4 ++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ecf0dab86..e784801bca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -324,6 +324,7 @@ jobs: with: upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_NAME }} + asset_name: ${{ env.LINUX_PACKAGE_NAME }} asset_content_type: application/octet-stream @@ -352,3 +353,12 @@ jobs: tags: ${{ secrets.DOCKER_HUB_USERNAME }}/appflowy_client:${{ github.ref_name }} cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max + + notify-discord: + runs-on: ubuntu-latest + needs: [build-for-linux, build-for-windows, build-for-macOS] + steps: + - name: Notify Discord + run: | + curl -H "Content-Type: application/json" -d '{"username": "release@appflowy", "content": "🎉 AppFlowy ${{ github.ref_name }} is available. https://github.com/AppFlowy-IO/AppFlowy/releases/tag/'${{ github.ref_name }}'"}' "https://discord.com/api/webhooks/${{ secrets.DISCORD }}" + shell: bash \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 25eef12f1c..fcacaed6c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Release Notes +## Version 0.1.2 - 03/28/2023 + +### Bug Fixes + +- Fix: update calendar selected range. +- Fix: duplicate view. + ## Version 0.1.1 - 03/21/2023 ### New features diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index 1fc240a42a..c7ac53e5c3 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -23,7 +23,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi" LIB_NAME = "dart_ffi" -CURRENT_APP_VERSION = "0.1.1" +CURRENT_APP_VERSION = "0.1.2" FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite" PRODUCT_NAME = "AppFlowy" # CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index b9d5b4a875..05d84a4d4c 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.1.1 +version: 0.1.2 environment: sdk: ">=2.18.0 <3.0.0" @@ -89,7 +89,6 @@ dependencies: google_fonts: ^3.0.1 file_picker: <=5.0.0 percent_indicator: ^4.0.1 - appflowy_editor_plugins: path: packages/appflowy_editor_plugins calendar_view: ^1.0.1 diff --git a/frontend/scripts/makefile/flutter.toml b/frontend/scripts/makefile/flutter.toml index 88632e43a4..7b7da3eea5 100644 --- a/frontend/scripts/makefile/flutter.toml +++ b/frontend/scripts/makefile/flutter.toml @@ -159,7 +159,7 @@ script = [ [tasks.create-release-archive-macos] script = [ - # TODO + # TODO # "cd ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/product/${VERSION}/${TARGET_OS}/Release/${PRODUCT_NAME}", # "tar -czf ${PRODUCT_NAME}-${TARGET_OS}-x86.tar.gz *" ] @@ -169,7 +169,7 @@ script = [""" cd appflowy_flutter/ flutter clean flutter pub get - flutter build ${TARGET_OS} --${BUILD_FLAG} --build-name=${APP_VERSION} + flutter build ${TARGET_OS} --${BUILD_FLAG} """] script_runner = "@shell" From 826a5787d04d09a3064c92501defdd001a21f88f Mon Sep 17 00:00:00 2001 From: Jonathan Rufus Samuel <70965472+JRS296@users.noreply.github.com> Date: Thu, 30 Mar 2023 08:48:58 +0530 Subject: [PATCH 19/37] fix: Fixed Italics via Single Asterisk (#2115) * Fixed Italics via Single Asterisk * Changed shift+asterisk to * * Mardown Syntax Function vaiable name changes + cleanup * feat: single asterisk to italic markdown command --------- Co-authored-by: Lucas.Xu --- .../markdown_syntax_to_styled_text.dart | 41 ++++++++++++++++ .../built_in_shortcut_events.dart | 5 ++ .../markdown_syntax_to_styled_text_test.dart | 49 +++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart index 1f798f4114..ae2c354389 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart @@ -310,6 +310,47 @@ ShortcutEventHandler underscoreToItalicHandler = (editorState, event) { return KeyEventResult.handled; }; +//Same functionality implemented via Asterisk - for italics +ShortcutEventHandler asteriskToItalicHandler = (editorState, event) { + final selectionService = editorState.service.selectionService; + final selection = selectionService.currentSelection.value; + final textNodes = selectionService.currentSelectedNodes.whereType(); + if (selection == null || !selection.isSingle || textNodes.length != 1) { + return KeyEventResult.ignored; + } + + final textNode = textNodes.first; + final text = textNode.toPlainText(); + // Determine if an 'asterisk' already exists in the text node and only once. + final firstAsterisk = text.indexOf('*'); + final lastAsterisk = text.lastIndexOf('*'); + if (firstAsterisk == -1 || + firstAsterisk != lastAsterisk || + firstAsterisk == selection.start.offset - 1) { + return KeyEventResult.ignored; + } + + final transaction = editorState.transaction + ..deleteText(textNode, firstAsterisk, 1) + ..formatText( + textNode, + firstAsterisk, + selection.end.offset - firstAsterisk - 1, + { + BuiltInAttributeKey.italic: true, + }, + ) + ..afterSelection = Selection.collapsed( + Position( + path: textNode.path, + offset: selection.end.offset - 1, + ), + ); + editorState.apply(transaction); + + return KeyEventResult.handled; +}; + ShortcutEventHandler doubleAsteriskToBoldHandler = (editorState, event) { final selectionService = editorState.service.selectionService; final selection = selectionService.currentSelection.value; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart index 3d8fa6839d..222b412e7c 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -307,6 +307,11 @@ List builtInShortcutEvents = [ character: '_', handler: underscoreToItalicHandler, ), + ShortcutEvent( + key: 'Asterisk to italic', + character: '*', + handler: asteriskToItalicHandler, + ), ShortcutEvent( key: 'Double asterisk to bold', character: '*', diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart index c7bb897034..28b27a1c29 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart @@ -441,4 +441,53 @@ void main() async { expect(textNode.toPlainText(), text); })); }); + + group('Convert single asterisk to italic', () { + testWidgets('Test Single Asterisk for Italics', (tester) async { + const text = '*Hello World'; + final editor = tester.editor..insertTextNode(text); + await editor.startTesting(); + + await editor.updateSelection( + Selection.single(path: [0], startOffset: text.length), + ); + + await editor.pressLogicKey(character: '*'); + + final textNode = editor.nodeAtPath([0]) as TextNode; + final allItalic = textNode.allSatisfyItalicInSelection( + Selection.single( + path: [0], + startOffset: 0, + endOffset: text.length - 1, // delete the first * + ), + ); + expect(allItalic, true); + }); + + testWidgets( + 'nothing happens if there\'re more than one * precede the current position', + (tester) async { + const text = '**Hello World'; + final editor = tester.editor..insertTextNode(text); + await editor.startTesting(); + + await editor.updateSelection( + Selection.single(path: [0], startOffset: text.length), + ); + + await editor.pressLogicKey(character: '*'); + await tester.pumpAndSettle(); + + final textNode = editor.nodeAtPath([0]) as TextNode; + final allItalic = textNode.allSatisfyItalicInSelection( + Selection.single( + path: [0], + startOffset: 0, + endOffset: text.length, // insert a new * + ), + ); + expect(allItalic, false); + }); + }); } From 55dc3c620fbf9a94041e96fd9a5af101e1a7c7b0 Mon Sep 17 00:00:00 2001 From: Destiny Saturday <84413505+DestinedCodes@users.noreply.github.com> Date: Thu, 30 Mar 2023 06:49:33 +0100 Subject: [PATCH 20/37] fix: referenced board keyword (#2139) --- .../document/presentation/plugins/board/board_menu_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/board/board_menu_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/board/board_menu_item.dart index c81efbb279..c81d394f7d 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/board/board_menu_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/board/board_menu_item.dart @@ -18,7 +18,7 @@ SelectionMenuItem boardMenuItem = SelectionMenuItem( ); }, // TODO(a-wallen): Translate keywords - keywords: ['referenced board', 'referenced kanban'], + keywords: ['referenced', 'board', 'kanban'], handler: (editorState, menuService, context) { showLinkToPageMenu( editorState, From 4bea2b4b54a13a6d7881495d41e1d75a1486b407 Mon Sep 17 00:00:00 2001 From: Destiny Saturday <84413505+DestinedCodes@users.noreply.github.com> Date: Thu, 30 Mar 2023 07:02:34 +0100 Subject: [PATCH 21/37] fix: referenced grid keywords (#2139) --- .../document/presentation/plugins/grid/grid_menu_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/grid/grid_menu_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/grid/grid_menu_item.dart index b2775543e9..b79162027b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/grid/grid_menu_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/grid/grid_menu_item.dart @@ -17,7 +17,7 @@ SelectionMenuItem gridMenuItem = SelectionMenuItem( : editorState.editorStyle.selectionMenuItemIconColor, ); }, - keywords: ['referenced grid'], + keywords: ['referenced', 'grid'], handler: (editorState, menuService, context) { showLinkToPageMenu( editorState, From 37f9134ee271ba4e9b22fdca66e3c93828068064 Mon Sep 17 00:00:00 2001 From: GitStart <1501599+gitstart@users.noreply.github.com> Date: Thu, 30 Mar 2023 06:08:11 +0000 Subject: [PATCH 22/37] fix: Replace Shortcuts icon in the help menu option to fix misalignment --- .../presentation/widgets/float_bubble/question_bubble.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index e6ecf09270..aae2882d3c 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -203,7 +203,7 @@ extension QuestionBubbleExtension on BubbleAction { case BubbleAction.debug: return '🐛'; case BubbleAction.shortcuts: - return '⌨️'; + return '📋'; } } } From 9fff00f731ae855a79df2f2d3e0e62c200e5c317 Mon Sep 17 00:00:00 2001 From: Yijing Huang Date: Thu, 30 Mar 2023 21:10:10 -0500 Subject: [PATCH 23/37] Fix: Null check error in FlowyHoverContainer (#2148) * fix: fix null check error in FlowyHoverContainer and delete unnecessary widget * chore: set child to required in FlowyHoverContainer * chore: delete unused import file --- .../widgets/row/accessory/cell_accessory.dart | 24 ------------------- .../lib/style_widget/hover.dart | 6 ++--- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart index 031f957ef3..4433c9f43b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart @@ -3,7 +3,6 @@ import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:flowy_infra/size.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -123,7 +122,6 @@ class _AccessoryHoverState extends State { @override Widget build(BuildContext context) { List children = [ - const _Background(), Padding(padding: widget.contentPadding, child: widget.child), ]; @@ -171,28 +169,6 @@ class AccessoryHoverState extends ChangeNotifier { bool get onHover => _onHover; } -class _Background extends StatelessWidget { - const _Background({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, state, child) { - if (state.onHover) { - return FlowyHoverContainer( - style: HoverStyle( - borderRadius: Corners.s6Border, - hoverColor: AFThemeExtension.of(context).lightGreyHover, - ), - ); - } else { - return const SizedBox(); - } - }, - ); - } -} - class CellAccessoryContainer extends StatelessWidget { final List accessories; const CellAccessoryContainer({required this.accessories, Key? key}) diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart index bf9d255e2f..687c62a4d9 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/hover.dart @@ -115,11 +115,11 @@ class HoverStyle { class FlowyHoverContainer extends StatelessWidget { final HoverStyle style; - final Widget? child; + final Widget child; const FlowyHoverContainer({ Key? key, - this.child, + required this.child, required this.style, }) : super(key: key); @@ -151,7 +151,7 @@ class FlowyHoverContainer extends StatelessWidget { .iconTheme .copyWith(color: Theme.of(context).colorScheme.onSurface), ), - child: child!, + child: child, ), ); } From fe524dbc78f34c68cb6ceb30342273daabbf7a53 Mon Sep 17 00:00:00 2001 From: Askarbek Zadauly Date: Sun, 2 Apr 2023 18:54:39 +0600 Subject: [PATCH 24/37] feat: move kanban blocks (#2022) * chore: add edit / create field test * chore: add delete field test * chore: change log class arguments * chore: delete/create row * chore: set tracing log to debug level * fix: filter notification with id * chore: add get single select type option data * fix: high cpu usage * chore: format code * chore: update tokio version * chore: config tokio runtime subscriber * chore: add profiling feature * chore: setup auto login * chore: fix tauri build * chore: (unstable) using controllers * fix: initially authenticated and serializable fix * fix: ci warning * ci: compile error * fix: new folder trash overflow * fix: min width for nav panel * fix: nav panel and main panel animation on hide menu * fix: highlight active page * fix: post merge fixes * fix: post merge fix * fix: remove warnings * fix: change IDatabaseField fix eslint errors * chore: create cell component for each field type * chore: move cell hook into custom cell component * chore: refactor row hook * chore: add tauri clean * chore: add tauri clean * chore: save offset top of nav items * chore: move constants * fix: nav item popup overflow * fix: page rename position * chore: remove offset top * chore: remove floating menu functions * chore: scroll down to new page * chore: smooth scroll and scroll to new folder * fix: breadcrumbs * chore: back and forward buttons nav scroll fix * chore: get board groups and rows * chore: set log level & remove empty line * fix: create kanban board row * fix: appflowy session name * chore: import beautiful dnd * bug: kanban new row * chore: update refs * fix: dispose group controller * fix: dispose cell controller * chore: move rows in group * chore: move row into other block * fix: groups observer dispose * chore: dnd reordering * chore: fix import references * chore: initial edit board modal * fix: kanban board rendering * chore: add column and edit text cell * chore: column rename * chore: edit row components reorganize * chore: don't show group by field * wip: edit cell type * chore: fade in, out * chore: change field type * chore: update editing cell * chore: fade in change * chore: cell options layout * fix: padding fixes for cell wrapper * fix: cell options positions * chore: cell options write to backend * fix: select options for new row * chore: edit url cell * chore: language button * fix: close popup on lang select * fix: save url cell * chore: date picker * chore: small code cleanups * chore: options in board * chore: move fields dnd --------- Co-authored-by: nathan Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com> Co-authored-by: appflowy --- frontend/appflowy_tauri/package.json | 8 +- .../_shared/EditRow/CellOptions.tsx | 34 +++ .../_shared/EditRow/CellOptionsPopup.tsx | 152 +++++++++++++ .../_shared/EditRow/ChangeFieldTypePopup.tsx | 71 ++++++ .../_shared/EditRow/DatePickerPopup.tsx | 97 ++++++++ .../_shared/EditRow/EditCellDate.tsx | 24 ++ .../_shared/EditRow/EditCellNumber.tsx | 29 +++ .../_shared/EditRow/EditCellText.tsx | 41 ++++ .../_shared/EditRow/EditCellUrl.tsx | 31 +++ .../_shared/EditRow/EditCellWrapper.tsx | 102 +++++++++ .../_shared/EditRow/EditCheckboxCell.tsx | 23 ++ .../_shared/EditRow/EditFieldPopup.tsx | 130 +++++++++++ .../components/_shared/EditRow/EditRow.tsx | 210 ++++++++++++++++++ .../_shared/EditRow/FieldTypeIcon.tsx | 24 ++ .../_shared/EditRow/FieldTypeName.tsx | 18 ++ .../_shared/LanguageSelectPopup.tsx | 5 +- .../_shared/database-hooks/loadField.ts | 2 +- .../_shared/database-hooks/useCell.ts | 39 ++-- .../_shared/database-hooks/useDatabase.ts | 44 +++- .../_shared/database-hooks/useRow.ts | 26 ++- .../components/_shared/svg/ArrowLeftSvg.tsx | 7 + .../components/_shared/svg/ArrowRightSvg.tsx | 7 + .../components/_shared/svg/CheckboxSvg.tsx | 13 ++ .../components/_shared/svg/CheckmarkSvg.tsx | 7 + .../components/_shared/svg/ClockSvg.tsx | 15 ++ .../components/_shared/svg/EditorCheckSvg.tsx | 8 + .../_shared/svg/EditorUncheckSvg.tsx | 7 + .../components/_shared/svg/MoreSvg.tsx | 10 + .../components/_shared/svg/SkipLeftSvg.tsx | 9 + .../components/_shared/svg/SkipRightSvg.tsx | 9 + .../appflowy_app/components/board/Board.tsx | 61 +++-- .../components/board/BoardBlock.tsx | 58 +++-- .../components/board/BoardCard.tsx | 52 +++-- .../components/board/BoardCell.tsx | 7 + .../components/board/BoardOptionsCell.tsx | 10 +- .../components/board/BoardTextCell.tsx | 14 +- .../components/board/BoardUrlCell.tsx | 29 +++ .../components/error/ErrorModal.tsx | 4 +- .../GridTableHeader/GridTableHeader.hooks.tsx | 2 +- .../grid/GridTableHeader/GridTableHeader.tsx | 2 +- .../layout/HeaderPanel/LanguageButton.tsx | 15 ++ .../layout/HeaderPanel/PageOptions.tsx | 5 +- .../NavigationPanel/FolderItem.hooks.ts | 2 +- .../NavigationPanel/NavigationPanel.hooks.ts | 2 +- .../layout/NavigationPanel/PageItem.tsx | 2 +- .../user/application/notifications/parser.ts | 4 +- .../notifications/user_listener.ts | 4 +- .../effects/database/cell/cell_bd_svc.ts | 5 +- .../effects/database/cell/cell_controller.ts | 1 + .../effects/database/cell/cell_observer.ts | 4 +- .../database/cell/controller_builder.ts | 2 +- .../effects/database/cell/data_parser.ts | 6 +- .../effects/database/cell/data_persistence.ts | 6 +- .../database/cell/select_option_bd_svc.ts | 4 +- .../effects/database/database_bd_svc.ts | 40 +++- .../effects/database/database_controller.ts | 33 ++- .../effects/database/field/field_bd_svc.ts | 4 +- .../database/field/field_controller.ts | 6 +- .../effects/database/field/field_observer.ts | 4 +- .../field/type_option/type_option_bd_svc.ts | 4 +- .../field/type_option/type_option_context.ts | 2 +- .../type_option/type_option_controller.ts | 6 +- .../database/group/group_controller.ts | 12 +- .../effects/database/group/group_observer.ts | 4 +- .../database/notifications/observer.ts | 4 +- .../effects/database/notifications/parser.ts | 4 +- .../stores/effects/database/row/row_bd_svc.ts | 4 +- .../stores/effects/database/row/row_cache.ts | 8 +- .../database/view/database_view_cache.ts | 3 +- .../database/view/view_row_observer.ts | 4 +- .../effects/document/document_bd_svc.ts | 6 +- .../stores/effects/folder/app/app_bd_svc.ts | 4 +- .../stores/effects/folder/app/app_observer.ts | 4 +- .../effects/folder/notifications/observer.ts | 4 +- .../effects/folder/notifications/parser.ts | 4 +- .../stores/effects/folder/view/view_bd_svc.ts | 4 +- .../effects/folder/view/view_observer.ts | 5 +- .../folder/workspace/workspace_bd_svc.ts | 4 +- .../folder/workspace/workspace_observer.ts | 4 +- .../stores/effects/user/user_bd_svc.ts | 6 +- .../stores/reducers/current-user/slice.ts | 2 +- .../stores/reducers/database/slice.ts | 4 +- .../reducers/folders/notifications/parser.ts | 4 +- .../stores/reducers/grid/slice.ts | 2 +- .../stores/reducers/pages/slice.ts | 2 +- frontend/appflowy_tauri/src/main.tsx | 1 + .../appflowy_tauri/src/styles/Calendar.css | 141 ++++++++++++ frontend/appflowy_tauri/tailwind.config.cjs | 10 +- 88 files changed, 1660 insertions(+), 196 deletions(-) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptions.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellNumber.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellText.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellUrl.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCheckboxCell.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeIcon.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeName.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowLeftSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowRightSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckboxSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckmarkSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorCheckSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorUncheckSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MoreSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipLeftSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipRightSvg.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/board/BoardUrlCell.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/LanguageButton.tsx create mode 100644 frontend/appflowy_tauri/src/styles/Calendar.css diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index 9cac4d87c2..6f9ba16df5 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -23,6 +23,7 @@ "@slate-yjs/core": "^0.3.1", "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.2.0", + "dayjs": "^1.11.7", "events": "^3.3.0", "google-protobuf": "^3.21.2", "i18next": "^22.4.10", @@ -32,6 +33,8 @@ "nanoid": "^4.0.0", "protoc-gen-ts": "^0.8.5", "react": "^18.2.0", + "react-beautiful-dnd": "^13.1.1", + "react-calendar": "^4.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^3.1.4", "react-i18next": "^12.2.0", @@ -44,8 +47,8 @@ "slate-react": "^0.91.9", "ts-results": "^3.3.0", "utf8": "^3.0.0", - "yjs": "^13.5.51", - "y-indexeddb": "^9.0.9" + "y-indexeddb": "^9.0.9", + "yjs": "^13.5.51" }, "devDependencies": { "@tauri-apps/cli": "^1.2.2", @@ -53,6 +56,7 @@ "@types/is-hotkey": "^0.1.7", "@types/node": "^18.7.10", "@types/react": "^18.0.15", + "@types/react-beautiful-dnd": "^13.1.3", "@types/react-dom": "^18.0.6", "@types/utf8": "^3.0.1", "@types/uuid": "^9.0.1", diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptions.tsx new file mode 100644 index 0000000000..63aeeab62a --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptions.tsx @@ -0,0 +1,34 @@ +import { SelectOptionCellDataPB } from '@/services/backend'; +import { getBgColor } from '$app/components/_shared/getColor'; +import { useRef } from 'react'; + +export const CellOptions = ({ + data, + onEditClick, +}: { + data: SelectOptionCellDataPB | undefined; + onEditClick: (left: number, top: number) => void; +}) => { + const ref = useRef(null); + + const onClick = () => { + if (!ref.current) return; + const { left, top } = ref.current.getBoundingClientRect(); + onEditClick(left, top); + }; + + return ( +
onClick()} + className={'flex flex-wrap items-center gap-2 px-4 py-2 text-xs text-black'} + > + {data?.select_options?.map((option, index) => ( +
+ {option?.name || ''} +
+ )) || ''} +   +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx new file mode 100644 index 0000000000..0a2b1cf09b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx @@ -0,0 +1,152 @@ +import { KeyboardEventHandler, useEffect, useRef, useState } from 'react'; +import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; +import { useCell } from '$app/components/_shared/database-hooks/useCell'; +import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; +import { FieldController } from '$app/stores/effects/database/field/field_controller'; +import { SelectOptionCellDataPB, SelectOptionColorPB, SelectOptionPB } from '@/services/backend'; +import { getBgColor } from '$app/components/_shared/getColor'; +import { useTranslation } from 'react-i18next'; +import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; +import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; +import { CloseSvg } from '$app/components/_shared/svg/CloseSvg'; +import useOutsideClick from '$app/components/_shared/useOutsideClick'; +import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; +import { useAppSelector } from '$app/stores/store'; +import { ISelectOptionType } from '$app/stores/reducers/database/slice'; + +export const CellOptionsPopup = ({ + top, + left, + cellIdentifier, + cellCache, + fieldController, + onOutsideClick, +}: { + top: number; + left: number; + cellIdentifier: CellIdentifier; + cellCache: CellCache; + fieldController: FieldController; + onOutsideClick: () => void; +}) => { + const ref = useRef(null); + const { t } = useTranslation(''); + const [adjustedTop, setAdjustedTop] = useState(-100); + const [value, setValue] = useState(''); + const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); + const databaseStore = useAppSelector((state) => state.database); + + useEffect(() => { + if (!ref.current) return; + const { height } = ref.current.getBoundingClientRect(); + if (top + height + 40 > window.innerHeight) { + setAdjustedTop(window.innerHeight - height - 40); + } else { + setAdjustedTop(top); + } + }, [ref, window, top, left]); + + useOutsideClick(ref, async () => { + onOutsideClick(); + }); + + const onKeyDown: KeyboardEventHandler = async (e) => { + if (e.key === 'Enter' && value.length > 0) { + await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: value }); + setValue(''); + } + }; + + const onUnselectOptionClick = async (option: SelectOptionPB) => { + await new SelectOptionCellBackendService(cellIdentifier).unselectOption([option.id]); + setValue(''); + }; + + const onToggleOptionClick = async (option: SelectOptionPB) => { + if ( + (data as SelectOptionCellDataPB | undefined)?.select_options?.find( + (selectedOption) => selectedOption.id === option.id + ) + ) { + await new SelectOptionCellBackendService(cellIdentifier).unselectOption([option.id]); + } else { + await new SelectOptionCellBackendService(cellIdentifier).selectOption([option.id]); + } + setValue(''); + }; + + useEffect(() => { + console.log('loaded data: ', data); + console.log('have stored ', databaseStore.fields[cellIdentifier.fieldId]); + }, [data]); + + return ( +
+
+
+
+ {(data as SelectOptionCellDataPB | undefined)?.select_options?.map((option, index) => ( +
+ {option?.name || ''} + +
+ )) || ''} +
+ setValue(e.target.value)} + placeholder={t('grid.selectOption.searchOption') || ''} + onKeyDown={onKeyDown} + /> +
{value.length}/30
+
+
+
{t('grid.selectOption.panelTitle') || ''}
+
+ {(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map( + (option, index) => ( +
+ onToggleOptionClick( + new SelectOptionPB({ + id: option.selectOptionId, + name: option.title, + color: option.color || SelectOptionColorPB.Purple, + }) + ) + } + className={ + 'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-main-secondary' + } + > +
{option.title}
+
+ {(data as SelectOptionCellDataPB | undefined)?.select_options?.find( + (selectedOption) => selectedOption.id === option.selectOptionId + ) && ( + + )} + +
+
+ ) + )} +
+
+
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx new file mode 100644 index 0000000000..da7e8b0375 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx @@ -0,0 +1,71 @@ +import { FieldType } from '@/services/backend'; +import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; +import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import useOutsideClick from '$app/components/_shared/useOutsideClick'; + +const typesOrder: FieldType[] = [ + FieldType.RichText, + FieldType.Number, + FieldType.DateTime, + FieldType.SingleSelect, + FieldType.MultiSelect, + FieldType.Checkbox, + FieldType.URL, + FieldType.Checklist, +]; + +export const ChangeFieldTypePopup = ({ + top, + right, + onClick, + onOutsideClick, +}: { + top: number; + right: number; + onClick: (newType: FieldType) => void; + onOutsideClick: () => void; +}) => { + const ref = useRef(null); + const [adjustedTop, setAdjustedTop] = useState(-100); + useOutsideClick(ref, async () => { + onOutsideClick(); + }); + + useEffect(() => { + if (!ref.current) return; + const { height } = ref.current.getBoundingClientRect(); + if (top + height > window.innerHeight) { + setAdjustedTop(window.innerHeight - height); + } else { + setAdjustedTop(top); + } + }, [ref, window, top, right]); + + return ( +
+
+ {typesOrder.map((t, i) => ( + + ))} +
+
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx new file mode 100644 index 0000000000..d7af34ec23 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx @@ -0,0 +1,97 @@ +import { useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; +import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; +import { FieldController } from '$app/stores/effects/database/field/field_controller'; +import useOutsideClick from '$app/components/_shared/useOutsideClick'; +import Calendar from 'react-calendar'; +import dayjs from 'dayjs'; +import { ClockSvg } from '$app/components/_shared/svg/ClockSvg'; +import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; +import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; +import { useCell } from '$app/components/_shared/database-hooks/useCell'; + +export const DatePickerPopup = ({ + left, + top, + cellIdentifier, + cellCache, + fieldController, + onOutsideClick, +}: { + left: number; + top: number; + cellIdentifier: CellIdentifier; + cellCache: CellCache; + fieldController: FieldController; + onOutsideClick: () => void; +}) => { + const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); + const ref = useRef(null); + const [adjustedTop, setAdjustedTop] = useState(-100); + // const [value, setValue] = useState(); + const { t } = useTranslation(''); + const [selectedDate, setSelectedDate] = useState(new Date()); + + useEffect(() => { + if (!ref.current) return; + const { height } = ref.current.getBoundingClientRect(); + if (top + height + 40 > window.innerHeight) { + setAdjustedTop(top - height - 40); + } else { + setAdjustedTop(top); + } + }, [ref, window, top, left]); + + useOutsideClick(ref, async () => { + onOutsideClick(); + }); + + useEffect(() => { + // console.log((data as DateCellDataPB).date); + // setSelectedDate(new Date((data as DateCellDataPB).date)); + }, [data]); + + const onChange = (v: Date | null | (Date | null)[]) => { + if (v instanceof Date) { + console.log(dayjs(v).format('YYYY-MM-DD')); + setSelectedDate(v); + // void cellController?.saveCellData(new DateCellDataPB({ date: dayjs(v).format('YYYY-MM-DD') })); + } + }; + + return ( +
+
+ onChange(d)} value={selectedDate} /> +
+
+
+
+ + + + {t('grid.field.includeTime')} +
+ + + +
+
+
+ + {t('grid.field.dateFormat')} & {t('grid.field.timeFormat')} + + + + +
+
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx new file mode 100644 index 0000000000..faf24eaf3a --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx @@ -0,0 +1,24 @@ +import { useRef } from 'react'; +import { DateCellDataPB } from '@/services/backend'; + +export const EditCellDate = ({ + data, + onEditClick, +}: { + data?: DateCellDataPB; + onEditClick: (left: number, top: number) => void; +}) => { + const ref = useRef(null); + + const onClick = () => { + if (!ref.current) return; + const { left, top } = ref.current.getBoundingClientRect(); + onEditClick(left, top); + }; + + return ( +
onClick()} className={'px-4 py-2'}> + {data?.date || <> } +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellNumber.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellNumber.tsx new file mode 100644 index 0000000000..205ddd9257 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellNumber.tsx @@ -0,0 +1,29 @@ +import { CellController } from '$app/stores/effects/database/cell/cell_controller'; +import { useEffect, useState } from 'react'; + +export const EditCellNumber = ({ + data, + cellController, +}: { + data: string | undefined; + cellController: CellController; +}) => { + const [value, setValue] = useState(''); + + useEffect(() => { + setValue(data || ''); + }, [data]); + + const save = async () => { + await cellController?.saveCellData(value); + }; + + return ( + setValue(e.target.value)} + onBlur={() => save()} + className={'w-full px-4 py-2'} + > + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellText.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellText.tsx new file mode 100644 index 0000000000..65c09e9880 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellText.tsx @@ -0,0 +1,41 @@ +import { CellController } from '$app/stores/effects/database/cell/cell_controller'; +import { useEffect, useState, KeyboardEvent, useMemo } from 'react'; + +export const EditCellText = ({ + data, + cellController, +}: { + data: string | undefined; + cellController: CellController; +}) => { + const [value, setValue] = useState(''); + const [contentRows, setContentRows] = useState(1); + + useEffect(() => { + setValue(data || ''); + }, [data]); + + useEffect(() => { + setContentRows(Math.max(1, (value || '').split('\n').length)); + }, [value]); + + const onTextFieldChange = async (v: string) => { + setValue(v); + }; + + const save = async () => { + await cellController?.saveCellData(value); + }; + + return ( +
+