Skip to content

Commit

Permalink
Merge branch 'main' into feat-zhiya
Browse files Browse the repository at this point in the history
* main:
  chore: add codecov token (AppFlowy-IO#352)
  feat: paste plaintext shortcut (AppFlowy-IO#338)
  fix: fixed unresponsive behaviour of context menu items (AppFlowy-IO#349)
  fix: fixed the issue of code block not being exported in markdown (AppFlowy-IO#347)
  chore: format code (AppFlowy-IO#345)
  feat: expose context in nodeBuilder (AppFlowy-IO#350)
  • Loading branch information
q200892907 committed Aug 3, 2023
2 parents 9adf7f4 + 6706292 commit 0896c8d
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 59 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ jobs:
fail_ci_if_error: true
verbose: true
os: ${{ matrix.os }}
token: ${{ secrets.CODECOV_TOKEN }}
attempt_limit: 10
attempt_delay: 10000
4 changes: 2 additions & 2 deletions lib/src/editor/block_component/standard_block_components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,6 @@ final List<CommandShortcutEvent> standardCommandShortcutEvents = [

// copy paste and cut
copyCommand,
pasteCommand,
cutCommand
...pasteCommands,
cutCommand,
];
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,13 @@ CommandShortcutEventHandler _copyCommandHandler = (editorState) {
// plain text.
final text = editorState.getTextInSelection(selection).join('\n');

// rich text.
// TODO: support rich text. the below code is not working.

// html
final nodes = editorState.getSelectedNodes(selection);
final document = Document.blank()..insert([0], nodes);
final html = documentToHTML(document);

final html = documentToHTML(
Document(
root: Node(
type: 'page',
children: nodes,
),
),
);
AppFlowyClipboard.setData(
text: text,
text: text.isEmpty ? null : text,
html: html.isEmpty ? null : html,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

final List<CommandShortcutEvent> pasteCommands = [
pasteCommand,
pasteTextWithoutFormattingCommand,
];

/// Paste.
///
/// - support
Expand All @@ -14,6 +19,52 @@ final CommandShortcutEvent pasteCommand = CommandShortcutEvent(
handler: _pasteCommandHandler,
);

final CommandShortcutEvent pasteTextWithoutFormattingCommand =
CommandShortcutEvent(
key: 'paste the content',
command: 'ctrl+shift+v',
macOSCommand: 'cmd+shift+v',
handler: _pasteTextWithoutFormattingCommandHandler,
);

CommandShortcutEventHandler _pasteTextWithoutFormattingCommandHandler =
(editorState) {
if (PlatformExtension.isMobile) {
assert(
false,
'pasteTextWithoutFormattingCommand is not supported on mobile platform.',
);
return KeyEventResult.ignored;
}

var selection = editorState.selection;
if (selection == null) {
return KeyEventResult.ignored;
}

// delete the selection first.
if (!selection.isCollapsed) {
editorState.deleteSelection(selection);
}

// fetch selection again.
selection = editorState.selection;
if (selection == null) {
return KeyEventResult.skipRemainingHandlers;
}
assert(selection.isCollapsed);

() async {
final data = await AppFlowyClipboard.getData();
final text = data.text;
if (text != null && text.isNotEmpty) {
handlePastePlainText(editorState, text);
}
}();

return KeyEventResult.handled;
};

CommandShortcutEventHandler _pasteCommandHandler = (editorState) {
if (PlatformExtension.isMobile) {
assert(false, 'pasteCommand is not supported on mobile platform.');
Expand All @@ -37,7 +88,6 @@ CommandShortcutEventHandler _pasteCommandHandler = (editorState) {
}
assert(selection.isCollapsed);

// TODO: paste the rich text.
() async {
final data = await AppFlowyClipboard.getData();
final text = data.text;
Expand Down
7 changes: 4 additions & 3 deletions lib/src/editor/selection_menu/selection_menu_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ class SelectionMenuItem {
required String name,
required IconData iconData,
required List<String> keywords,
required Node Function(EditorState editorState) nodeBuilder,
required Node Function(EditorState editorState, BuildContext context)
nodeBuilder,
bool Function(EditorState editorState, Node node)? insertBefore,
bool Function(EditorState editorState, Node node)? replace,
Selection? Function(
Expand All @@ -105,7 +106,7 @@ class SelectionMenuItem {
size: 18.0,
),
keywords: keywords,
handler: (editorState, _, __) {
handler: (editorState, _, context) {
final selection = editorState.selection;
if (selection == null || !selection.isCollapsed) {
return;
Expand All @@ -115,7 +116,7 @@ class SelectionMenuItem {
if (node == null || delta == null) {
return;
}
final newNode = nodeBuilder(editorState);
final newNode = nodeBuilder(editorState, context);
final transaction = editorState.transaction;
final bReplace = replace?.call(editorState, node) ?? false;
final bInsertBefore = insertBefore?.call(editorState, node) ?? false;
Expand Down
17 changes: 7 additions & 10 deletions lib/src/editor_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -298,17 +298,14 @@ class EditorState {
}
final startIndex = node == nodes.first ? selection.startIndex : 0;
final endIndex = node == nodes.last ? selection.endIndex : delta.length;
final Attributes attributes = node.attributes;
attributes.remove(ParagraphBlockKeys.delta);
attributes.addAll(
{ParagraphBlockKeys.delta: delta.slice(startIndex, endIndex).toJson()},
res.add(
node.copyWith(
attributes: {
...node.attributes,
blockComponentDelta: delta.slice(startIndex, endIndex).toJson()
},
),
);
final copyNode = Node(
type: node.type,
attributes: attributes,
children: node.children,
);
res.add(copyNode);
}

return res;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ class CodeBlockNodeParser extends NodeParser {
const CodeBlockNodeParser();

@override
String get id => 'code_block';
String get id => 'code';

@override
String transform(Node node) {
assert(node.type == 'code_block');
assert(node.type == 'code');

final delta = node.delta;
final language = node.attributes['language'] ?? '';
if (delta == null) {
throw Exception('Delta is null');
}
final markdown = DeltaMarkdownEncoder().convert(delta);
final result = '```\n$markdown\n```';
final result = '```$language\n$markdown\n```';
final suffix = node.next == null ? '' : '\n';

return '$result$suffix';
Expand Down
14 changes: 11 additions & 3 deletions lib/src/service/context_menu/built_in_context_menu_item.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import 'package:appflowy_editor/src/service/context_menu/context_menu.dart';

import '../internal_key_event_handlers/copy_paste_handler.dart';

final builtInContextMenuItems = [
[
// cut
ContextMenuItem(
name: 'Cut',
onPressed: (editorState) {},
onPressed: (editorState) {
handleCut(editorState);
},
),
// copy
ContextMenuItem(
name: 'Copy',
onPressed: (editorState) {},
onPressed: (editorState) {
handleCopy(editorState);
},
),
// Paste
ContextMenuItem(
name: 'Paste',
onPressed: (editorState) {},
onPressed: (editorState) {
handlePaste(editorState);
},
),
],
];
48 changes: 24 additions & 24 deletions lib/src/service/internal_key_event_handlers/copy_paste_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,16 @@ void pasteHTML(EditorState editorState, String html) {
}

Log.keyboard.debug('paste html: $html');
final htmltoNodes = htmlToDocument(html);

if (htmltoNodes.isEmpty) {
final htmlToNodes = htmlToDocument(html);
if (htmlToNodes.isEmpty) {
return;
}

_pasteMultipleLinesInText(
editorState,
selection.start.offset,
htmltoNodes.root.children.toList(),
htmlToNodes.root.children.toList(),
);
}

Expand Down Expand Up @@ -154,36 +154,36 @@ void _pasteMultipleLinesInText(
editorState.apply(transaction);
}

final (firstnode, afternode) = sliceNode(node, offset);
final (firstNode, afterNode) = sliceNode(node, offset);
if (nodes.length == 1 && nodes.first.type == node.type) {
transaction.deleteNode(node);
final List<dynamic> newdelta = firstnode.delta != null
? firstnode.delta!.toJson()
final List<dynamic> newDelta = firstNode.delta != null
? firstNode.delta!.toJson()
: Delta().toJson();
final List<Node> childrens = [];
childrens.addAll(firstnode.children);
final List<Node> children = [];
children.addAll(firstNode.children);

if (nodes.first.delta != null &&
nodes.first.delta != null &&
nodes.first.delta!.isNotEmpty) {
newdelta.addAll(nodes.first.delta!.toJson());
childrens.addAll(nodes.first.children);
newDelta.addAll(nodes.first.delta!.toJson());
children.addAll(nodes.first.children);
}
if (afternode != null &&
afternode.delta != null &&
afternode.delta!.isNotEmpty) {
newdelta.addAll(afternode.delta!.toJson());
childrens.addAll(afternode.children);
if (afterNode != null &&
afterNode.delta != null &&
afterNode.delta!.isNotEmpty) {
newDelta.addAll(afterNode.delta!.toJson());
children.addAll(afterNode.children);
}

transaction.insertNodes(afterSelection.end.path, [
Node(
type: firstnode.type,
children: childrens,
attributes: firstnode.attributes
type: firstNode.type,
children: children,
attributes: firstNode.attributes
..remove(ParagraphBlockKeys.delta)
..addAll(
{ParagraphBlockKeys.delta: Delta.fromJson(newdelta).toJson()},
{ParagraphBlockKeys.delta: Delta.fromJson(newDelta).toJson()},
),
)
]);
Expand All @@ -195,12 +195,12 @@ void _pasteMultipleLinesInText(
transaction.insertNodes([
path.first + 1
], [
firstnode,
firstNode,
...nodes,
if (afternode != null &&
afternode.delta != null &&
afternode.delta!.isNotEmpty)
afternode,
if (afterNode != null &&
afterNode.delta != null &&
afterNode.delta!.isNotEmpty)
afterNode,
]);
editorState.apply(transaction);
return;
Expand Down
Loading

0 comments on commit 0896c8d

Please sign in to comment.