appflowy-editor
appflowy-editor copied to clipboard
[Bug] Unable to find the cursor after the image upload
Bug Description
Can't able to find the cursor, if I had uploaded the image , for example if I uploaded an image, I should get the cursor after the image blinking up, so that I can delete it with my back space etc, or else if I moved my cursor to any text and again clicked beside the image uploaded I should get the cursor.
Unable to understand this, any idea could help!
Not only that, if I used the AppFLowyEditor widget with editor state having an blank document, then also unable to make cursor on selection in the editor to start writing.
Thanks!
How to Reproduce
Cursor should be placed beside any custom widget uploaded, and also when unfocused and focused by clicking beside that widget, the cursor should appear!
Expected Behavior
Cursor should be placed beside any custom widget uploaded, and also when unfocused and focused by clicking beside that widget, the cursor should appear!
Operating System
Windows
AppFlowy Editor Version(s)
1.2.2
Screenshots
Additional Context
No response
Any help @LucasXu0 ?
Not only that, if I used the AppFLowyEditor widget with editor state having an blank document, then also unable to make cursor on selection in the editor to start writing.
There're two options to solve your problem.
- create the blank document with an initial text node.
factory Document.blank({bool withInitialText = false}) {}
- listen to the transaction changes and customize your rules based on your case.
// subscribe to the document change from the editor
_subscription = editorState.transactionStream.listen((event) async {
final time = event.$1;
if (time != TransactionTime.before) {
return;
}
await _transactionAdapter.apply(event.$2, editorState);
// check if the document is empty.
applyRules();
});
Future<void> applyRules() async {
ensureAtLeastOneParagraphExists();
ensureLastNodeIsEditable();
}
Future<void> ensureLastNodeIsEditable() async {
final editorState = this.editorState;
if (editorState == null) {
return;
}
final document = editorState.document;
final lastNode = document.root.children.lastOrNull;
if (lastNode == null || lastNode.delta == null) {
final transaction = editorState.transaction;
transaction.insertNode([document.root.children.length], paragraphNode());
await editorState.apply(transaction);
}
}
Future<void> ensureAtLeastOneParagraphExists() async {
final editorState = this.editorState;
if (editorState == null) {
return;
}
final document = editorState.document;
if (document.root.children.isEmpty) {
final transaction = editorState.transaction;
transaction.insertNode([0], paragraphNode());
transaction.afterSelection = Selection.collapse([0], 0);
await editorState.apply(transaction);
}
}
for example if I uploaded an image, I should get the cursor after the image blinking up, so that I can delete it with my back space etc
The default behavior for the backspace key is to delete the currently selected text and move the cursor to the previous instance containing text.
So you have to customize your own backspace command in your case. Also, you can delete the image by selecting it.
Here's the solution on the AppFlowy app, which displays the delete button in the top-right corner when the user hovers over the image.
Exactly where can we customize the rules? i.e, while inserting or can you tell where the place this code could works and the second point, for example take the math equation block component, how to delete that widget by selecting, do we need to write any code for selecting by pressing on the widget inserted?
Thanks @LucasXu0 for taking time and explaining these things, this helps a lot!
Exactly where can we customize the rules?
You can listen on the editorState.transactionStream
before passing it AppFlowyEditor.
For example,
// listen on the stream
_subscription = editorState.transactionStream.listen((event) async {
final time = event.$1;
if (time != TransactionTime.before) {
return;
}
// customize your rules here!
ensureAtLeastOneParagraphExists();
// other rules
// ...
});
Future<void> ensureAtLeastOneParagraphExists() async {
final editorState = this.editorState;
if (editorState == null) {
return;
}
final document = editorState.document;
if (document.root.children.isEmpty) {
final transaction = editorState.transaction;
transaction.insertNode([0], paragraphNode());
transaction.afterSelection = Selection.collapse([0], 0);
await editorState.apply(transaction);
}
}
for example take the math equation block component, how to delete that widget by selecting, do we need to write any code for selecting by pressing on the widget inserted?
The default behavior when pressing on the math equation block is to edit the formula. If you want to delete it by making a selection, you need to enable the extension of the selection behavior using the Selectable
mixin. Instead of that solution, I strongly suggest that you implement deletion by adding a delete icon above it.
Here's the sample code for customizing a menu for the block component. https://github.com/AppFlowy-IO/appflowy-editor/pull/265
You can listen on the editorState.transactionStream before passing it AppFlowyEditor.
for example, if this is the method to upload the math equation widget node to insert inside the editor and I want a cursor to be displayed beside the equation block after it is inserted inside the editor
void updateMathEquation(String mathEquation, BuildContext context) async {
if (mathEquation == formula) {
dismiss(context); return;
}
final transaction = editorState.transaction
..updateNode(
widget.node,
{
MathEquationBlockKeys.formula: mathEquation,
},
);
editorState.apply(transaction);
}
my query is where could we use the subscription here, and apply the transaction? and also didn't understand the _transactionAdapter also, can you specify any example using this or else use case in the above method,
It could help understand more @LucasXu0 , please!
If you can control the transaction, then just set the afterSelection
within your function.
final transaction = editorState.transaction
..updateNode(
widget.node,
{
MathEquationBlockKeys.formula: mathEquation,
},
);
transaction.afterSelection = Selection(node.path.next, 0); // <- here
editorState.apply(transaction);
I think it is
final transaction = editorState.transaction
..updateNode(
widget.node,
{
MathEquationBlockKeys.formula: mathEquation,
},
);
transaction.afterSelection = Selection.collapsed(node.path.next, 0); // <- Is it right?
editorState.apply(transaction);
Another thing, transaction adapter and stream, is used in simple editor page , Appflowy editor widget? , or else any use case or example for that, just want to know where we can use subscription in our main code.
// listen on the stream
_subscription = editorState.transactionStream.listen((event) async {
final time = event.$1;
if (time != TransactionTime.before) {
return;
}
// customize your rules here!
ensureAtLeastOneParagraphExists();
// other rules
// ...
});
Future<void> ensureAtLeastOneParagraphExists() async {
final editorState = this.editorState;
if (editorState == null) {
return;
}
final document = editorState.document;
if (document.root.children.isEmpty) {
final transaction = editorState.transaction;
transaction.insertNode([0], paragraphNode());
transaction.afterSelection = Selection.collapse([0], 0);
await editorState.apply(transaction);
}
}
As of now, after image upload, Cursor not working, instead the the uploaded image is selecting, but no cursor below it after applying this changes also, Just uploaded a video.
https://github.com/AppFlowy-IO/appflowy-editor/assets/98700279/7802058e-91cc-400d-920b-639d7d04c047
Two points not getting from it:
- when I am tapping the editor instead of getting cursor I was defaultly selecting the image.
- After the upload dailog box dismissed, the focus is getting off and we have to manually tap for getting the cursor
If I select and delete it by backspace, the error in the console is
Any thoughts? @LucasXu0
I would like to know if there's any update for this issue.
Hi, @LucasXu0 Could you please suggest a fix for this cursor issue?
Meanwhile, I am also having an issue with toolbar not appearing when text is selected. I would really appreciate a guideline to implement it.
Problem
Expected Behavior