Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 0d611ed4 authored by Corey Bryant's avatar Corey Bryant Committed by GitHub
Browse files

Merge pull request #9940 from rafaeltonholo/uplift/beta/9939/revert-9399

uplift(beta): Revert "Propose a solution for #3172, initially covering long URL on mobile phone screens"
parents 191cf40c 82ea8194
Loading
Loading
Loading
Loading
+25 −65
Original line number Diff line number Diff line
package com.fsck.k9.message.html

import app.k9mail.html.cleaner.HtmlHeadProvider
import org.intellij.lang.annotations.Language

class DisplayHtml(private val settings: HtmlSettings) : HtmlHeadProvider {
    override val headHtml: String
        get() {
            @Language("HTML")
            val html = """
                <meta name="viewport" content="width=device-width"/>
                ${cssStyleGlobal()}
                ${cssStylePre()}
                ${cssStyleSignature()}
            """.trimIndent()

            return html
            return """<meta name="viewport" content="width=device-width"/>""" +
                cssStyleTheme() +
                cssStylePre() +
                cssStyleSignature()
        }

    fun wrapStatusMessage(status: CharSequence): String {
        @Language("HTML")
        val html = """
            <div style="text-align:center; color: grey;">$status</div>
        """.trimIndent()

        return wrapMessageContent(html)
        return wrapMessageContent("<div style=\"text-align:center; color: grey;\">$status</div>")
    }

    @Language("HTML")
    fun wrapMessageContent(messageContent: CharSequence): String {
        // Include a meta tag so the WebView will not use a fixed viewport width of 980 px
        return """
            <html dir="auto">
                <head>
                    $headHtml
                </head>
                <body>
                    $messageContent
                </body>
            </html>
        """.trimIndent()
        return "<html dir=\"auto\"><head><meta name=\"viewport\" content=\"width=device-width\"/>" +
            cssStyleTheme() +
            cssStylePre() +
            "</head><body>" +
            messageContent +
            "</body></html>"
    }

    /**
     * Dynamically generates a CSS style block that applies global rules to all elements (`*` selector).
     *
     * The style enforces word-breaking and overflow wrapping to prevent content overflow
     * and ensures long text strings break correctly without causing horizontal scrolling.
     *
     * @return A `<style>` element string that can be dynamically injected into the HTML `<head>`
     * to apply these global styles when rendering messages.
     */
    @Language("HTML")
    private fun cssStyleGlobal(): String {
        return """
            <style type="text/css">
                * {
                    word-break: break-word;
                    overflow-wrap: break-word;
    private fun cssStyleTheme(): String {
        return if (settings.useDarkMode) {
            // TODO: Don't hardcode these values. Inject them via HtmlSettings.
            "<style type=\"text/css\">" +
                "* { background: #121212 ! important; color: #F3F3F3 !important }" +
                ":link, :link * { color: #CCFF33 !important }" +
                ":visited, :visited * { color: #551A8B !important }</style> "
        } else {
            ""
        }
            </style>
        """.trimIndent()
    }

    /**
@@ -70,30 +45,15 @@ class DisplayHtml(private val settings: HtmlSettings) : HtmlHeadProvider {
     * @return A `<style>` element that can be dynamically included in the HTML `<head>` element when messages are
     * displayed.
     */
    @Language("HTML")
    private fun cssStylePre(): String {
        val font = if (settings.useFixedWidthFont) "monospace" else "sans-serif"

        return """
            <style type="text/css">
                pre.${EmailTextToHtml.K9MAIL_CSS_CLASS} {
                    white-space: pre-wrap;
                    word-wrap: break-word;
                    font-family: $font;
                    margin-top: 0px;
                }
            </style>
        """.trimIndent()
        return "<style type=\"text/css\"> pre." + EmailTextToHtml.K9MAIL_CSS_CLASS +
            " {white-space: pre-wrap; word-wrap:break-word; " +
            "font-family: " + font + "; margin-top: 0px}</style>"
    }

    @Language("HTML")
    private fun cssStyleSignature(): String {
        return """
            <style type="text/css">
                .k9mail-signature {
                    opacity: 0.5;
                }
            </style>
        """.trimIndent()
        return """<style type="text/css">.k9mail-signature { opacity: 0.5 }</style>"""
    }
}
+9 −55
Original line number Diff line number Diff line
@@ -2,15 +2,13 @@ package com.fsck.k9.message.html

import assertk.Assert
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.hasSize
import assertk.assertions.isEqualTo
import org.jsoup.Jsoup
import org.junit.Test

class DisplayHtmlTest {
    val htmlSettings = HtmlSettings(useDarkMode = false, useFixedWidthFont = false)
    val displayHtml = DisplayHtml(htmlSettings)
    val displayHtml = DisplayHtml(HtmlSettings(useDarkMode = false, useFixedWidthFont = false))

    @Test
    fun wrapMessageContent_addsViewportMetaElement() {
@@ -27,45 +25,19 @@ class DisplayHtmlTest {
    }

    @Test
    fun wrapMessageContent_addsPreCSSStyles() {
    fun wrapMessageContent_addsPreCSS() {
        val html = displayHtml.wrapMessageContent("Some text")

        assertThat(html).containsHtmlElement("head > style", 3)
        assertThat(html).containsHtmlElement("head > style")
    }

    @Test
    fun wrapMessageContent_addsGlobalStyleRules() {
        val html = displayHtml.wrapMessageContent("test")

        assertThat(html).containsStyleRulesFor(
            selector = "*",
            "word-break: break-word;",
            "overflow-wrap: break-word;",
        )
    }
    fun wrapMessageContent_whenDarkMessageViewTheme_addsDarkThemeCSS() {
        val darkModeDisplayHtml = DisplayHtml(HtmlSettings(useDarkMode = true, useFixedWidthFont = false))

    @Test
    fun wrapMessageContent_addsPreCSS() {
        val html = displayHtml.wrapMessageContent("test")
        val expectedFont = if (htmlSettings.useFixedWidthFont) "monospace" else "sans-serif"

        assertThat(html).containsStyleRulesFor(
            selector = "pre.${EmailTextToHtml.K9MAIL_CSS_CLASS}",
            "white-space: pre-wrap;",
            "word-wrap: break-word;",
            "font-family: $expectedFont;",
            "margin-top: 0px;",
        )
    }

    @Test
    fun wrapMessageContent_addsSignatureStyleRules() {
        val html = displayHtml.wrapMessageContent("test")
        val html = darkModeDisplayHtml.wrapMessageContent("Some text")

        assertThat(html).containsStyleRulesFor(
            selector = ".k9mail-signature",
            "opacity: 0.5;",
        )
        assertThat(html).htmlElements("head > style").hasSize(2)
    }

    @Test
@@ -77,26 +49,8 @@ class DisplayHtmlTest {
        assertThat(html).bodyText().isEqualTo(content)
    }

    private fun Assert<String>.containsStyleRulesFor(selector: String, vararg expectedRules: String) = given { html ->
        val styleContent = Jsoup.parse(html)
            .select("style")
            .joinToString("\n") { it.data() }

        val selectorPattern = Regex.escape(selector).replace("\\*", "\\\\*")
        val selectorBlock = Regex("$selectorPattern\\s*\\{([^}]*)\\}", RegexOption.MULTILINE)
            .find(styleContent)
            ?.groupValues?.get(1)
            ?.trim()

        requireNotNull(selectorBlock) { "No style block found for selector: $selector" }

        expectedRules.forEach { rule ->
            assertThat(selectorBlock).contains(rule)
        }
    }

    private fun Assert<String>.containsHtmlElement(cssQuery: String, expectedCount: Int = 1) = given { actual ->
        assertThat(actual).htmlElements(cssQuery).hasSize(expectedCount)
    private fun Assert<String>.containsHtmlElement(cssQuery: String) = given { actual ->
        assertThat(actual).htmlElements(cssQuery).hasSize(1)
    }

    private fun Assert<String>.htmlElements(cssQuery: String) = transform { html ->