Skip to content

Commit 10048da

Browse files
committed
Merge branch 'main' into local_ai_global_config
2 parents 815bb11 + 7372f55 commit 10048da

19 files changed

Lines changed: 1008 additions & 148 deletions

File tree

frontend/Makefile.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
2626
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
2727
CARGO_MAKE_CRATE_NAME = "dart-ffi"
2828
LIB_NAME = "dart_ffi"
29-
APPFLOWY_VERSION = "0.8.7"
29+
APPFLOWY_VERSION = "0.8.8"
3030
FLUTTER_DESKTOP_FEATURES = "dart"
3131
PRODUCT_NAME = "AppFlowy"
3232
MACOSX_DEPLOYMENT_TARGET = "11.0"

frontend/appflowy_flutter/integration_test/desktop/uncategorized/emoji_shortcut_test.dart

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import 'dart:io';
22

3+
import 'package:appflowy/plugins/emoji/emoji_handler.dart';
34
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
45
import 'package:appflowy_editor/appflowy_editor.dart';
56
import 'package:appflowy_editor/src/editor/editor_component/service/editor.dart';
7+
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
68
import 'package:flutter/services.dart';
79
import 'package:flutter_test/flutter_test.dart';
810
import 'package:integration_test/integration_test.dart';
@@ -39,4 +41,114 @@ void main() {
3941
expect(find.byType(EmojiSelectionMenu), findsOneWidget);
4042
});
4143
});
44+
45+
group('insert emoji by colon', () {
46+
Future<void> createNewDocumentAndShowEmojiList(WidgetTester tester) async {
47+
await tester.initializeAppFlowy();
48+
await tester.tapAnonymousSignInButton();
49+
await tester.createNewPageWithNameUnderParent();
50+
await tester.editor.tapLineOfEditorAt(0);
51+
await tester.ime.insertText(':');
52+
await tester.pumpAndSettle(Duration(seconds: 1));
53+
}
54+
55+
testWidgets('insert with click', (tester) async {
56+
await createNewDocumentAndShowEmojiList(tester);
57+
58+
/// emoji list is showing
59+
final emojiHandler = find.byType(EmojiHandler);
60+
expect(emojiHandler, findsOneWidget);
61+
final emojiButtons =
62+
find.descendant(of: emojiHandler, matching: find.byType(FlowyButton));
63+
final firstTextFinder = find.descendant(
64+
of: emojiButtons.first,
65+
matching: find.byType(FlowyText),
66+
);
67+
final emojiText =
68+
(firstTextFinder.evaluate().first.widget as FlowyText).text;
69+
70+
/// click first emoji item
71+
await tester.tapButton(emojiButtons.first);
72+
final firstNode =
73+
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
74+
75+
/// except the emoji is in document
76+
expect(emojiText.contains(firstNode.delta!.toPlainText()), true);
77+
});
78+
79+
testWidgets('insert with arrow and enter', (tester) async {
80+
await createNewDocumentAndShowEmojiList(tester);
81+
82+
/// emoji list is showing
83+
final emojiHandler = find.byType(EmojiHandler);
84+
expect(emojiHandler, findsOneWidget);
85+
final emojiButtons =
86+
find.descendant(of: emojiHandler, matching: find.byType(FlowyButton));
87+
88+
/// tap arrow down and arrow up
89+
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowUp);
90+
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowDown);
91+
92+
final firstTextFinder = find.descendant(
93+
of: emojiButtons.first,
94+
matching: find.byType(FlowyText),
95+
);
96+
final emojiText =
97+
(firstTextFinder.evaluate().first.widget as FlowyText).text;
98+
99+
/// tap enter
100+
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
101+
final firstNode =
102+
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
103+
104+
/// except the emoji is in document
105+
expect(emojiText.contains(firstNode.delta!.toPlainText()), true);
106+
});
107+
108+
testWidgets('insert with searching', (tester) async {
109+
await createNewDocumentAndShowEmojiList(tester);
110+
111+
/// search for `smiling eyes`, IME is not working, use keyboard input
112+
final searchText = [
113+
LogicalKeyboardKey.keyS,
114+
LogicalKeyboardKey.keyM,
115+
LogicalKeyboardKey.keyI,
116+
LogicalKeyboardKey.keyL,
117+
LogicalKeyboardKey.keyI,
118+
LogicalKeyboardKey.keyN,
119+
LogicalKeyboardKey.keyG,
120+
LogicalKeyboardKey.space,
121+
LogicalKeyboardKey.keyE,
122+
LogicalKeyboardKey.keyY,
123+
LogicalKeyboardKey.keyE,
124+
LogicalKeyboardKey.keyS,
125+
];
126+
127+
for (final key in searchText) {
128+
await tester.simulateKeyEvent(key);
129+
}
130+
131+
/// tap enter
132+
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
133+
final firstNode =
134+
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
135+
136+
/// except the emoji is in document
137+
expect(firstNode.delta!.toPlainText().contains('😄'), true);
138+
});
139+
140+
testWidgets('start searching with sapce', (tester) async {
141+
await createNewDocumentAndShowEmojiList(tester);
142+
143+
/// emoji list is showing
144+
final emojiHandler = find.byType(EmojiHandler);
145+
expect(emojiHandler, findsOneWidget);
146+
147+
/// input space
148+
await tester.simulateKeyEvent(LogicalKeyboardKey.space);
149+
150+
/// emoji list is dismissed
151+
expect(emojiHandler, findsNothing);
152+
});
153+
});
42154
}

frontend/appflowy_flutter/integration_test/desktop/uncategorized/uncategorized_test_runner_1.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ void main() {
1313
hotkeys_test.main();
1414
emoji_shortcut_test.main();
1515
hotkeys_test.main();
16-
emoji_shortcut_test.main();
1716
share_markdown_test.main();
1817
import_files_test.main();
1918
zoom_in_out_test.main();

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ class BlockActionOptionCubit extends Cubit<BlockActionOptionState> {
469469
blockComponentDelta: newDelta.toJson(),
470470
},
471471
children: [
472-
...node.children,
472+
...node.children.map((e) => e.deepCopy()),
473473
...insertedNodes.map((e) => e.deepCopy()),
474474
],
475475
);

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/ai/ai_writer_block_component.dart

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -423,28 +423,25 @@ class SecondaryContentArea extends StatelessWidget {
423423
mainAxisSize: MainAxisSize.min,
424424
crossAxisAlignment: CrossAxisAlignment.start,
425425
children: [
426+
const VSpace(8.0),
427+
Container(
428+
height: 24.0,
429+
padding: EdgeInsets.symmetric(horizontal: 14.0),
430+
alignment: AlignmentDirectional.centerStart,
431+
child: FlowyText(
432+
command.i18n,
433+
fontSize: 12,
434+
fontWeight: FontWeight.w600,
435+
color: Color(0xFF666D76),
436+
),
437+
),
438+
const VSpace(4.0),
426439
Flexible(
427440
child: SingleChildScrollView(
428441
physics: ClampingScrollPhysics(),
429-
padding: EdgeInsets.only(top: 8.0, left: 14.0, right: 14.0),
430-
child: Column(
431-
mainAxisSize: MainAxisSize.min,
432-
children: [
433-
Container(
434-
height: 24.0,
435-
alignment: AlignmentDirectional.centerStart,
436-
child: FlowyText(
437-
command.i18n,
438-
fontSize: 12,
439-
fontWeight: FontWeight.w600,
440-
color: Color(0xFF666D76),
441-
),
442-
),
443-
const VSpace(4.0),
444-
AIMarkdownText(
445-
markdown: markdownText,
446-
),
447-
],
442+
padding: EdgeInsets.symmetric(horizontal: 14.0),
443+
child: AIMarkdownText(
444+
markdown: markdownText,
448445
),
449446
),
450447
),

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/ai/operations/ai_writer_block_operations.dart

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ Future<void> removeAiWriterNode(
4848
);
4949
}
5050

51-
void formatSelection(
51+
Future<void> formatSelection(
5252
EditorState editorState,
5353
Selection selection,
54-
Transaction transaction,
5554
ApplySuggestionFormatType formatType,
56-
) {
55+
) async {
5756
final nodes = editorState.getNodesInSelection(selection).toList();
5857
if (nodes.isEmpty) {
5958
return;
6059
}
60+
final transaction = editorState.transaction;
6161

6262
if (nodes.length == 1) {
6363
final node = nodes.removeAt(0);
@@ -103,25 +103,43 @@ void formatSelection(
103103
}
104104

105105
transaction.compose();
106+
await editorState.apply(
107+
transaction,
108+
options: ApplyOptions(
109+
inMemoryUpdate: true,
110+
recordUndo: false,
111+
),
112+
withUpdateSelection: false,
113+
);
106114
}
107115

108-
Position ensurePreviousNodeIsEmptyParagraph(
116+
Future<Position> ensurePreviousNodeIsEmptyParagraph(
109117
EditorState editorState,
110118
Node aiWriterNode,
111-
Transaction transaction,
112-
) {
119+
) async {
113120
final previous = aiWriterNode.previous;
114121
final needsEmptyParagraphNode = previous == null ||
115122
previous.type != ParagraphBlockKeys.type ||
116123
(previous.delta?.toPlainText().isNotEmpty ?? false);
117124

118125
final Position position;
126+
final transaction = editorState.transaction;
127+
119128
if (needsEmptyParagraphNode) {
120129
position = Position(path: aiWriterNode.path);
121130
transaction.insertNode(aiWriterNode.path, paragraphNode());
122131
} else {
123132
position = Position(path: previous.path);
124133
}
134+
transaction.afterSelection = Selection.collapsed(position);
135+
136+
await editorState.apply(
137+
transaction,
138+
options: ApplyOptions(
139+
inMemoryUpdate: true,
140+
recordUndo: false,
141+
),
142+
);
125143

126144
return position;
127145
}

0 commit comments

Comments
 (0)