Summary
Creating a note, opening it in the extended (rich) editor and closing it without entering anything can leave the empty note saved in the database, where it also counts towards the statistics. An empty new note is supposed to be discarded on close, which is what happens in the simple editor.
Steps to reproduce
- Settings → Interaction → enable the extended editor.
- Create a new note.
- Do not type anything.
- Leave the screen.
Expected: the empty note is discarded, exactly as an empty note from the simple editor is.
Actual: the note is saved, appears in the list and is included in the statistics.
Cause
NotePresenter.hasMeaningfulContent treats any valueJson other than null, blank or the literal "[]" as content:
// Extended editor JSON
String json = note.getValueJson();
return json != null && !json.trim().isEmpty() && !json.equals("[]");
valueJson holds the Editor.js block array (EditorJsonUtils parses it with new JSONArray(json)). As soon as the editor writes a document containing a block — an empty paragraph is enough — the string is no longer "[]", so an untouched note reads as meaningful. Two things follow, both in NotePresenter:
closeActivity() skips the "new empty note → delete" branch, because it is guarded by newNoteKey && !hasMeaningfulContent(targetNote).
needsSave() returns true for the same reason, so the empty note is actively written.
The code already carries a comment acknowledging this, immediately above the extended-editor branch in closeActivity():
// Extended editor "empty JSON" check is WRONG — replace with targetNote
if (extendedEditor && !hasMeaningfulContent(targetNote)) {
Suggested fix
Decide emptiness from the parsed blocks rather than the raw string: a note is empty when every block carries no text and no attachment. EditorJsonUtils already parses the array and extracts plain text and attachments, so the check can reuse that path instead of comparing the serialized form. Comparing strings will keep breaking as the editor changes what it serializes for an empty document (block ids, a time field, a trailing empty paragraph).
Worth covering with a unit test per shape: [], a single empty paragraph, a paragraph containing only whitespace, and a block list whose only entry is an attachment (which is meaningful).
Notes on verification
Reproduced from the report, not by me: on 2.6.54 installed from Play I created a note with the extended editor active, left it untouched, closed it — and the note was correctly discarded, both with and without tapping inside the editor first. So the defect needs the editor to have persisted a non-empty block array, which did not happen on that path on that build. The code defect above is real regardless of which path triggers it, and it is the reason an empty rich note can survive at all.
Environment: 2.6.54, Pixel 7a, Android 17.
Summary
Creating a note, opening it in the extended (rich) editor and closing it without entering anything can leave the empty note saved in the database, where it also counts towards the statistics. An empty new note is supposed to be discarded on close, which is what happens in the simple editor.
Steps to reproduce
Expected: the empty note is discarded, exactly as an empty note from the simple editor is.
Actual: the note is saved, appears in the list and is included in the statistics.
Cause
NotePresenter.hasMeaningfulContenttreats anyvalueJsonother thannull, blank or the literal"[]"as content:valueJsonholds the Editor.js block array (EditorJsonUtilsparses it withnew JSONArray(json)). As soon as the editor writes a document containing a block — an empty paragraph is enough — the string is no longer"[]", so an untouched note reads as meaningful. Two things follow, both inNotePresenter:closeActivity()skips the "new empty note → delete" branch, because it is guarded bynewNoteKey && !hasMeaningfulContent(targetNote).needsSave()returnstruefor the same reason, so the empty note is actively written.The code already carries a comment acknowledging this, immediately above the extended-editor branch in
closeActivity():Suggested fix
Decide emptiness from the parsed blocks rather than the raw string: a note is empty when every block carries no text and no attachment.
EditorJsonUtilsalready parses the array and extracts plain text and attachments, so the check can reuse that path instead of comparing the serialized form. Comparing strings will keep breaking as the editor changes what it serializes for an empty document (block ids, atimefield, a trailing empty paragraph).Worth covering with a unit test per shape:
[], a single empty paragraph, a paragraph containing only whitespace, and a block list whose only entry is an attachment (which is meaningful).Notes on verification
Reproduced from the report, not by me: on 2.6.54 installed from Play I created a note with the extended editor active, left it untouched, closed it — and the note was correctly discarded, both with and without tapping inside the editor first. So the defect needs the editor to have persisted a non-empty block array, which did not happen on that path on that build. The code defect above is real regardless of which path triggers it, and it is the reason an empty rich note can survive at all.
Environment: 2.6.54, Pixel 7a, Android 17.