Repository: python-discord/site

Issue:
Make newlines visible in the deleted messages front-end
The deleted messages front-end currently doesn't display newlines if they're consecutive, i.e., if the lines are otherwise empty (`"\n\n\n\n\n\n\n\n\n"`). This makes it difficult to interpret what actually happened in chat. A good solution to this would be to make newlines character visible using the `↵` character, printed in a light gray color (similar to how IDEs do it).

**before**
![2019-10-27_18-30](https://user-images.githubusercontent.com/33516116/67638694-d53a1c80-f8e7-11e9-9971-d022a1d90a17.png)

**proposed after**
![2019-10-27_18-29](https://user-images.githubusercontent.com/33516116/67638696-e08d4800-f8e7-11e9-8a7d-acf41e2b1f8a.png)



Discussion:

Resolving patch:
diff --git a/pydis_site/apps/staff/templatetags/deletedmessage_filters.py b/pydis_site/apps/staff/templatetags/deletedmessage_filters.py
--- a/pydis_site/apps/staff/templatetags/deletedmessage_filters.py
+++ b/pydis_site/apps/staff/templatetags/deletedmessage_filters.py
@@ -7,11 +7,22 @@
 
 @register.filter
 def hex_colour(color: int) -> str:
-    """Converts an integer representation of a colour to the RGB hex value."""
-    return f"#{color:0>6X}"
+    """
+    Converts an integer representation of a colour to the RGB hex value.
+
+    As we are using a Discord dark theme analogue, black colours are returned as white instead.
+    """
+    colour = f"#{color:0>6X}"
+    return colour if colour != "#000000" else "#FFFFFF"
 
 
 @register.filter
 def footer_datetime(timestamp: str) -> datetime:
     """Takes an embed timestamp and returns a timezone-aware datetime object."""
     return datetime.fromisoformat(timestamp)
+
+
+@register.filter
+def visible_newlines(text: str) -> str:
+    """Takes an embed timestamp and returns a timezone-aware datetime object."""
+    return text.replace("\n", " <span class='has-text-grey'>↵</span><br>")


Test patch:
diff --git a/pydis_site/apps/staff/tests/test_deletedmessage_filters.py b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py
--- a/pydis_site/apps/staff/tests/test_deletedmessage_filters.py
+++ b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py
@@ -18,16 +18,49 @@ class Colour(enum.IntEnum):
 
 class DeletedMessageFilterTests(TestCase):
     def test_hex_colour_filter(self):
-        self.assertEqual(deletedmessage_filters.hex_colour(Colour.BLACK), "#000000")
-        self.assertEqual(deletedmessage_filters.hex_colour(Colour.BLUE), "#0000FF")
-        self.assertEqual(deletedmessage_filters.hex_colour(Colour.GREEN), "#00FF00")
-        self.assertEqual(deletedmessage_filters.hex_colour(Colour.RED), "#FF0000")
-        self.assertEqual(deletedmessage_filters.hex_colour(Colour.WHITE), "#FFFFFF")
+        """The filter should produce the correct hex values from the integer representations."""
+        test_values = (
+            (Colour.BLUE, "#0000FF"),
+            (Colour.GREEN, "#00FF00"),
+            (Colour.RED, "#FF0000"),
+            (Colour.WHITE, "#FFFFFF"),
+
+            # Since we're using a "Discord dark theme"-like front-end, show black text as white.
+            (Colour.BLACK, "#FFFFFF"),
+        )
+
+        for colour, hex_value in test_values:
+            with self.subTest(colour=colour, hex_value=hex_value):
+                self.assertEqual(deletedmessage_filters.hex_colour(colour), hex_value)
 
     def test_footer_datetime_filter(self):
+        """The filter should parse the ISO-datetime string and return a timezone-aware datetime."""
         datetime_aware = timezone.now()
         iso_string = datetime_aware.isoformat()
 
         datetime_returned = deletedmessage_filters.footer_datetime(iso_string)
         self.assertTrue(timezone.is_aware(datetime_returned))
         self.assertEqual(datetime_aware, datetime_returned)
+
+    def test_visual_newlines_filter(self):
+        """The filter should replace newline characters by newline character and html linebreak."""
+        html_br = " <span class='has-text-grey'>↵</span><br>"
+
+        test_values = (
+            (
+                "Hello, this line does not contain a linebreak",
+                "Hello, this line does not contain a linebreak"
+            ),
+            (
+                "A single linebreak\nin a string",
+                f"A single linebreak{html_br}in a string"
+            ),
+            (
+                "Consecutive linebreaks\n\n\nwork, too",
+                f"Consecutive linebreaks{html_br}{html_br}{html_br}work, too"
+            )
+        )
+
+        for input_, expected_output in test_values:
+            with self.subTest(input=input_, expected_output=expected_output):
+                self.assertEqual(deletedmessage_filters.visible_newlines(input_), expected_output)