diff --git a/app/boxes.py b/app/boxes.py index ce0c7a9..6667545 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -42,8 +42,8 @@ from app.utils import webmentions from app.utils.datetime import as_utc from app.utils.datetime import now from app.utils.datetime import parse_isoformat -from app.utils.text import slugify from app.utils.facepile import WebmentionReply +from app.utils.text import slugify AnyboxObject = models.InboxObject | models.OutboxObject @@ -2592,7 +2592,7 @@ class ReplyTreeNode: @property def published_at(self) -> datetime.datetime: if self.ap_object: - return self.ap_object.ap_published_at + return self.ap_object.ap_published_at # type: ignore elif self.wm_reply: return self.wm_reply.published_at else: diff --git a/app/main.py b/app/main.py index 6e26891..01e20a1 100644 --- a/app/main.py +++ b/app/main.py @@ -74,8 +74,8 @@ from app.uploads import UPLOAD_DIR from app.utils import pagination from app.utils.emoji import EMOJIS_BY_NAME from app.utils.facepile import Face -from app.utils.facepile import merge_faces from app.utils.facepile import WebmentionReply +from app.utils.facepile import merge_faces from app.utils.highlight import HIGHLIGHT_CSS_HASH from app.utils.url import check_url from app.webfinger import get_remote_follow_template @@ -837,19 +837,21 @@ def _merge_faces_from_inbox_object_and_webmentions( def _merge_replies( reply_tree_node: boxes.ReplyTreeNode, webmentions: list[models.Webmention], -) -> None: +) -> boxes.ReplyTreeNode: + # TODO: return None as we update the object in place webmention_replies = [] for wm in [ - wm for wm in webmentions - if wm.webmention_type == models.WebmentionType.REPLY + wm for wm in webmentions if wm.webmention_type == models.WebmentionType.REPLY ]: if rep := WebmentionReply.from_webmention(wm): - webmention_replies.append(boxes.ReplyTreeNode( - ap_object=None, - wm_reply=rep, - is_requested=False, - children=[], - )) + webmention_replies.append( + boxes.ReplyTreeNode( + ap_object=None, + wm_reply=rep, + is_requested=False, + children=[], + ) + ) reply_tree_node.children = sorted( reply_tree_node.children + webmention_replies, diff --git a/app/scss/main.scss b/app/scss/main.scss index dc99b1c..31400f6 100644 --- a/app/scss/main.scss +++ b/app/scss/main.scss @@ -541,3 +541,7 @@ a.label-btn { content: ': '; } } + +.margin-top-20 { + margin-top: 20px; +} diff --git a/app/templates.py b/app/templates.py index c850622..e33c588 100644 --- a/app/templates.py +++ b/app/templates.py @@ -335,6 +335,14 @@ def _clean_html(html: str, note: Object) -> str: raise +def _clean_html_wm(html: str) -> str: + return bleach.clean( + html, + attributes=ALLOWED_ATTRIBUTES, + strip=True, + ) + + def _timeago(original_dt: datetime) -> str: dt = original_dt if dt.tzinfo: @@ -411,6 +419,7 @@ def _poll_item_pct(item: ap.RawObject, voters_count: int) -> int: _templates.env.filters["domain"] = _filter_domain _templates.env.filters["media_proxy_url"] = _media_proxy_url _templates.env.filters["clean_html"] = _clean_html +_templates.env.filters["clean_html_wm"] = _clean_html_wm _templates.env.filters["timeago"] = _timeago _templates.env.filters["format_date"] = _format_date _templates.env.filters["has_media_type"] = _has_media_type diff --git a/app/templates/utils.html b/app/templates/utils.html index bfe3c3a..3926177 100644 --- a/app/templates/utils.html +++ b/app/templates/utils.html @@ -443,7 +443,40 @@ {% macro display_webmention_reply(wm_reply) %} {% block display_webmention_reply scoped %} -{{ wm_reply }} + +
+
+
+ {{ wm_reply.face.name }}'s avatar +
+ +
{{ wm_reply.face.name | clean_html_wm | safe }}
+
{{ wm_reply.face.url | truncate(64, True) }}
+
+
+ +

in reply to + this note +

+ +
+
+ {{ wm_reply.content | clean_html_wm | safe }} +
+
+ + +
+ {% endblock %} {% endmacro %} diff --git a/app/utils/facepile.py b/app/utils/facepile.py index 5aa56a6..6dda213 100644 --- a/app/utils/facepile.py +++ b/app/utils/facepile.py @@ -9,8 +9,8 @@ from app import media from app.models import InboxObject from app.models import Webmention from app.models import WebmentionType -from app.utils.url import make_abs from app.utils.datetime import parse_isoformat +from app.utils.url import make_abs @dataclass @@ -40,7 +40,9 @@ class Face: return cls( ap_actor_id=None, url=( - item["properties"]["url"][0] if item["properties"].get("url") else webmention.source + item["properties"]["url"][0] + if item["properties"].get("url") + else webmention.source ), name=item["properties"]["name"][0], picture_url=media.resized_media_url( @@ -95,7 +97,9 @@ def _parse_face(webmention: Webmention, items: list[dict[str, Any]]) -> Face | N return Face( ap_actor_id=None, url=( - items["properties"]["url"][0] if item["properties"].get("url") else webmention.source + item["properties"]["url"][0] + if item["properties"].get("url") + else webmention.source ), name=item["properties"]["name"][0], picture_url=media.resized_media_url( @@ -112,6 +116,8 @@ def _parse_face(webmention: Webmention, items: list[dict[str, Any]]) -> Face | N ) break + return None + @dataclass class WebmentionReply: @@ -119,9 +125,10 @@ class WebmentionReply: content: str url: str published_at: datetime.datetime + in_reply_to: str @classmethod - def from_webmention(cls, webmention: Webmention) -> "WebmentionReply": + def from_webmention(cls, webmention: Webmention) -> Optional["WebmentionReply"]: if webmention.webmention_type != WebmentionType.REPLY: raise ValueError(f"Unexpected webmention {webmention.id}") @@ -143,9 +150,12 @@ class WebmentionReply: published_at=parse_isoformat( item["properties"]["published"][0] ).replace(tzinfo=None), + in_reply_to=webmention.target, # type: ignore ) except Exception: logger.exception( f"Failed to build Face for webmention id={webmention.id}" ) break + + return None