fix: could not insert link on the Web

This commit is contained in:
Lucas.Xu 2022-09-28 23:01:22 +08:00
parent ab0131c19c
commit 99cb2430f7
5 changed files with 124 additions and 32 deletions

View File

@ -3,29 +3,25 @@ import 'package:appflowy_editor/src/document/attributes.dart';
import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/document/node.dart';
import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/path.dart';
import 'package:appflowy_editor/src/document/selection.dart';
import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/editor_state.dart';
Future<void> formatBuiltInTextAttributes( Future<void> formatBuiltInTextAttributes(
EditorState editorState, EditorState editorState,
String key, String key,
Attributes attributes, { Attributes attributes, {
Selection? selection,
Path? path, Path? path,
TextNode? textNode, TextNode? textNode,
}) async { }) async {
final result = getTextNodeToBeFormatted(
editorState,
path: path,
textNode: textNode,
);
if (BuiltInAttributeKey.globalStyleKeys.contains(key)) { if (BuiltInAttributeKey.globalStyleKeys.contains(key)) {
assert(!(path != null && textNode != null));
assert(!(path == null && textNode == null));
TextNode formattedTextNode;
if (textNode != null) {
formattedTextNode = textNode;
} else if (path != null) {
formattedTextNode = editorState.document.nodeAtPath(path) as TextNode;
} else {
throw Exception('path and textNode cannot be null at the same time');
}
// remove all the existing style // remove all the existing style
final newAttributes = formattedTextNode.attributes final newAttributes = result.attributes
..removeWhere((key, value) { ..removeWhere((key, value) {
if (BuiltInAttributeKey.globalStyleKeys.contains(key)) { if (BuiltInAttributeKey.globalStyleKeys.contains(key)) {
return true; return true;
@ -41,6 +37,13 @@ Future<void> formatBuiltInTextAttributes(
newAttributes, newAttributes,
textNode: textNode, textNode: textNode,
); );
} else if (BuiltInAttributeKey.partialStyleKeys.contains(key)) {
return updateTextNodeDeltaAttributes(
editorState,
selection,
attributes,
textNode: textNode,
);
} }
} }
@ -60,3 +63,20 @@ Future<void> formatTextToCheckbox(
textNode: textNode, textNode: textNode,
); );
} }
Future<void> formatLinkInText(
EditorState editorState,
String? link, {
Path? path,
TextNode? textNode,
}) async {
return formatBuiltInTextAttributes(
editorState,
BuiltInAttributeKey.href,
{
BuiltInAttributeKey.href: link,
},
path: path,
textNode: textNode,
);
}

View File

@ -1,6 +1,9 @@
import 'dart:async';
import 'package:appflowy_editor/src/document/attributes.dart'; import 'package:appflowy_editor/src/document/attributes.dart';
import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/document/node.dart';
import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/path.dart';
import 'package:appflowy_editor/src/document/selection.dart';
import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/editor_state.dart';
import 'package:appflowy_editor/src/operation/transaction_builder.dart'; import 'package:appflowy_editor/src/operation/transaction_builder.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -11,24 +14,90 @@ Future<void> updateTextNodeAttributes(
Path? path, Path? path,
TextNode? textNode, TextNode? textNode,
}) async { }) async {
assert(!(path != null && textNode != null)); final result = getTextNodeToBeFormatted(
assert(!(path == null && textNode == null)); editorState,
path: path,
textNode: textNode,
);
TextNode formattedTextNode; final completer = Completer<void>();
if (textNode != null) {
formattedTextNode = textNode;
} else if (path != null) {
formattedTextNode = editorState.document.nodeAtPath(path) as TextNode;
} else {
throw Exception('path and textNode cannot be null at the same time');
}
TransactionBuilder(editorState) TransactionBuilder(editorState)
..updateNode(formattedTextNode, attributes) ..updateNode(result, attributes)
..commit(); ..commit();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
print('AAAAAAAAAAAAAA'); completer.complete();
return;
}); });
return completer.future;
}
Future<void> updateTextNodeDeltaAttributes(
EditorState editorState,
Selection? selection,
Attributes attributes, {
Path? path,
TextNode? textNode,
}) {
final result = getTextNodeToBeFormatted(
editorState,
path: path,
textNode: textNode,
);
final newSelection = _getSelection(editorState, selection: selection);
final completer = Completer<void>();
TransactionBuilder(editorState)
..formatText(
result,
newSelection.startIndex,
newSelection.length,
attributes,
)
..commit();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
completer.complete();
});
return completer.future;
}
// get formatted [TextNode]
TextNode getTextNodeToBeFormatted(
EditorState editorState, {
Path? path,
TextNode? textNode,
}) {
assert(!(path != null && textNode != null));
assert(!(path == null && textNode == null));
TextNode result;
if (textNode != null) {
result = textNode;
} else if (path != null) {
result = editorState.document.nodeAtPath(path) as TextNode;
} else {
throw Exception('path and textNode cannot be null at the same time');
}
return result;
}
Selection _getSelection(
EditorState editorState, {
Selection? selection,
}) {
final currentSelection =
editorState.service.selectionService.currentSelection.value;
Selection result;
if (selection != null) {
result = selection;
} else if (currentSelection != null) {
result = currentSelection;
} else {
throw Exception('path and textNode cannot be null at the same time');
}
return result;
} }

View File

@ -53,6 +53,10 @@ class Selection {
Selection get reversed => copyWith(start: end, end: start); Selection get reversed => copyWith(start: end, end: start);
int get startIndex => normalize.start.offset;
int get endIndex => normalize.end.offset;
int get length => endIndex - startIndex;
Selection collapse({bool atStart = false}) { Selection collapse({bool atStart = false}) {
if (atStart) { if (atStart) {
return Selection(start: start, end: start); return Selection(start: start, end: start);

View File

@ -74,8 +74,8 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
padding: iconPadding, padding: iconPadding,
name: check ? 'check' : 'uncheck', name: check ? 'check' : 'uncheck',
), ),
onTap: () { onTap: () async {
formatTextToCheckbox( await formatTextToCheckbox(
widget.editorState, widget.editorState,
!check, !check,
textNode: widget.textNode, textNode: widget.textNode,

View File

@ -1,4 +1,5 @@
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/commands/format_built_in_text.dart';
import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart';
import 'package:appflowy_editor/src/infra/flowy_svg.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart';
import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; import 'package:appflowy_editor/src/render/link_menu/link_menu.dart';
@ -345,11 +346,8 @@ void showLinkMenu(
onOpenLink: () async { onOpenLink: () async {
await safeLaunchUrl(linkText); await safeLaunchUrl(linkText);
}, },
onSubmitted: (text) { onSubmitted: (text) async {
TransactionBuilder(editorState) await formatLinkInText(editorState, text, textNode: textNode);
..formatText(
textNode, index, length, {BuiltInAttributeKey.href: text})
..commit();
_dismissLinkMenu(); _dismissLinkMenu();
}, },
onCopyLink: () { onCopyLink: () {
@ -377,6 +375,7 @@ void showLinkMenu(
Overlay.of(context)?.insert(_linkMenuOverlay!); Overlay.of(context)?.insert(_linkMenuOverlay!);
editorState.service.scrollService?.disable(); editorState.service.scrollService?.disable();
editorState.service.keyboardService?.disable();
editorState.service.selectionService.currentSelection editorState.service.selectionService.currentSelection
.addListener(_dismissLinkMenu); .addListener(_dismissLinkMenu);
} }