diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt index 8a5dc558b6..aca5f827a7 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt @@ -264,7 +264,13 @@ class RichTextParser { emojis: Map, tags: ImmutableListOfLists, ): ImmutableList { - val lines = content.split('\n') + // Trailing spaces and newlines would otherwise produce empty trailing + // paragraphs, each rendered as a blank line between the last visible + // word and the end of the component. + val trimmedContent = content.trimEnd() + if (trimmedContent.isEmpty()) return persistentListOf() + + val lines = trimmedContent.split('\n') val paragraphSegments = ArrayList(lines.size) lines.forEach { paragraph -> diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt index 68361d41c3..e287c158b6 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt @@ -4087,11 +4087,55 @@ class RichTextParserTest { assertTrue(state.mediaList.isEmpty()) assertTrue(state.customEmoji.isEmpty()) assertEquals( - "\nHi,\nhow\n\n\n are you doing?\n", + "\nHi,\nhow\n\n\n are you doing?", state.paragraphs.joinToString("\n") { it.words.joinToString(" ") { it.segmentText } }, ) } + @Test + fun testTrailingWhitespaceIsRemoved() { + val state = + RichTextParser().parseText("Hi, how are you doing? \n\n \n", EmptyTagList, null) + + assertEquals(1, state.paragraphs.size) + assertEquals( + "Hi, how are you doing?", + state.paragraphs + .firstOrNull() + ?.words + ?.firstOrNull() + ?.segmentText, + ) + } + + @Test + fun testTrailingNewLinesAfterImageAreRemoved() { + val state = + RichTextParser().parseText( + "Check this out\nhttps://cdn.nostr.build/image.jpg\n\n\n", + EmptyTagList, + null, + ) + + assertEquals(2, state.paragraphs.size) + val lastWord = + state.paragraphs + .last() + .words + .single() + assertEquals( + "Image(https://cdn.nostr.build/image.jpg)", + "${lastWord::class.simpleName!!.replace("Segment", "")}(${lastWord.segmentText})", + ) + } + + @Test + fun testWhitespaceOnlyContentProducesNoParagraphs() { + val state = RichTextParser().parseText(" \n \n ", EmptyTagList, null) + + assertTrue(state.paragraphs.isEmpty()) + } + @Test fun testMultiLine() { val text =