More progess on webmention replies
parent
066f5ec900
commit
9ee3f3b971
|
@ -42,8 +42,8 @@ from app.utils import webmentions
|
||||||
from app.utils.datetime import as_utc
|
from app.utils.datetime import as_utc
|
||||||
from app.utils.datetime import now
|
from app.utils.datetime import now
|
||||||
from app.utils.datetime import parse_isoformat
|
from app.utils.datetime import parse_isoformat
|
||||||
from app.utils.text import slugify
|
|
||||||
from app.utils.facepile import WebmentionReply
|
from app.utils.facepile import WebmentionReply
|
||||||
|
from app.utils.text import slugify
|
||||||
|
|
||||||
AnyboxObject = models.InboxObject | models.OutboxObject
|
AnyboxObject = models.InboxObject | models.OutboxObject
|
||||||
|
|
||||||
|
@ -2592,7 +2592,7 @@ class ReplyTreeNode:
|
||||||
@property
|
@property
|
||||||
def published_at(self) -> datetime.datetime:
|
def published_at(self) -> datetime.datetime:
|
||||||
if self.ap_object:
|
if self.ap_object:
|
||||||
return self.ap_object.ap_published_at
|
return self.ap_object.ap_published_at # type: ignore
|
||||||
elif self.wm_reply:
|
elif self.wm_reply:
|
||||||
return self.wm_reply.published_at
|
return self.wm_reply.published_at
|
||||||
else:
|
else:
|
||||||
|
|
22
app/main.py
22
app/main.py
|
@ -74,8 +74,8 @@ from app.uploads import UPLOAD_DIR
|
||||||
from app.utils import pagination
|
from app.utils import pagination
|
||||||
from app.utils.emoji import EMOJIS_BY_NAME
|
from app.utils.emoji import EMOJIS_BY_NAME
|
||||||
from app.utils.facepile import Face
|
from app.utils.facepile import Face
|
||||||
from app.utils.facepile import merge_faces
|
|
||||||
from app.utils.facepile import WebmentionReply
|
from app.utils.facepile import WebmentionReply
|
||||||
|
from app.utils.facepile import merge_faces
|
||||||
from app.utils.highlight import HIGHLIGHT_CSS_HASH
|
from app.utils.highlight import HIGHLIGHT_CSS_HASH
|
||||||
from app.utils.url import check_url
|
from app.utils.url import check_url
|
||||||
from app.webfinger import get_remote_follow_template
|
from app.webfinger import get_remote_follow_template
|
||||||
|
@ -837,19 +837,21 @@ def _merge_faces_from_inbox_object_and_webmentions(
|
||||||
def _merge_replies(
|
def _merge_replies(
|
||||||
reply_tree_node: boxes.ReplyTreeNode,
|
reply_tree_node: boxes.ReplyTreeNode,
|
||||||
webmentions: list[models.Webmention],
|
webmentions: list[models.Webmention],
|
||||||
) -> None:
|
) -> boxes.ReplyTreeNode:
|
||||||
|
# TODO: return None as we update the object in place
|
||||||
webmention_replies = []
|
webmention_replies = []
|
||||||
for wm in [
|
for wm in [
|
||||||
wm for wm in webmentions
|
wm for wm in webmentions if wm.webmention_type == models.WebmentionType.REPLY
|
||||||
if wm.webmention_type == models.WebmentionType.REPLY
|
|
||||||
]:
|
]:
|
||||||
if rep := WebmentionReply.from_webmention(wm):
|
if rep := WebmentionReply.from_webmention(wm):
|
||||||
webmention_replies.append(boxes.ReplyTreeNode(
|
webmention_replies.append(
|
||||||
ap_object=None,
|
boxes.ReplyTreeNode(
|
||||||
wm_reply=rep,
|
ap_object=None,
|
||||||
is_requested=False,
|
wm_reply=rep,
|
||||||
children=[],
|
is_requested=False,
|
||||||
))
|
children=[],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
reply_tree_node.children = sorted(
|
reply_tree_node.children = sorted(
|
||||||
reply_tree_node.children + webmention_replies,
|
reply_tree_node.children + webmention_replies,
|
||||||
|
|
|
@ -541,3 +541,7 @@ a.label-btn {
|
||||||
content: ': ';
|
content: ': ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.margin-top-20 {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
|
@ -335,6 +335,14 @@ def _clean_html(html: str, note: Object) -> str:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_html_wm(html: str) -> str:
|
||||||
|
return bleach.clean(
|
||||||
|
html,
|
||||||
|
attributes=ALLOWED_ATTRIBUTES,
|
||||||
|
strip=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _timeago(original_dt: datetime) -> str:
|
def _timeago(original_dt: datetime) -> str:
|
||||||
dt = original_dt
|
dt = original_dt
|
||||||
if dt.tzinfo:
|
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["domain"] = _filter_domain
|
||||||
_templates.env.filters["media_proxy_url"] = _media_proxy_url
|
_templates.env.filters["media_proxy_url"] = _media_proxy_url
|
||||||
_templates.env.filters["clean_html"] = _clean_html
|
_templates.env.filters["clean_html"] = _clean_html
|
||||||
|
_templates.env.filters["clean_html_wm"] = _clean_html_wm
|
||||||
_templates.env.filters["timeago"] = _timeago
|
_templates.env.filters["timeago"] = _timeago
|
||||||
_templates.env.filters["format_date"] = _format_date
|
_templates.env.filters["format_date"] = _format_date
|
||||||
_templates.env.filters["has_media_type"] = _has_media_type
|
_templates.env.filters["has_media_type"] = _has_media_type
|
||||||
|
|
|
@ -443,7 +443,40 @@
|
||||||
|
|
||||||
{% macro display_webmention_reply(wm_reply) %}
|
{% macro display_webmention_reply(wm_reply) %}
|
||||||
{% block display_webmention_reply scoped %}
|
{% block display_webmention_reply scoped %}
|
||||||
{{ wm_reply }}
|
|
||||||
|
<div class="ap-object">
|
||||||
|
<div class="actor-box h-card p-author">
|
||||||
|
<div class="icon-box">
|
||||||
|
<img src="{{ wm_reply.face.picture_url }}" alt="{{ wm_reply.face.name }}'s avatar" class="actor-icon u-photo">
|
||||||
|
</div>
|
||||||
|
<a href="{{ wm_reply.face.url }}" class="u-url">
|
||||||
|
<div><strong class="p-name">{{ wm_reply.face.name | clean_html_wm | safe }}</strong></div>
|
||||||
|
<div class="actor-handle">{{ wm_reply.face.url | truncate(64, True) }}</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="in-reply-to">in reply to <a href="{{ wm_reply.in_reply_to }}" title="{{ wm_reply.in_reply_to }}" rel="nofollow">
|
||||||
|
this note
|
||||||
|
</a></p>
|
||||||
|
|
||||||
|
<div class="obj-content margin-top-20">
|
||||||
|
<div class="e-content">
|
||||||
|
{{ wm_reply.content | clean_html_wm | safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="flexbox activity-bar margin-top-20">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<div><a href="{{ wm_reply.url }}" rel="nofollow" class="object-permalink u-url u-uid">permalink</a></div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<time class="dt-published" datetime="{{ wm_reply.published_at.replace(microsecond=0).isoformat() }}" title="{{ wm_reply.published_at.replace(microsecond=0).isoformat() }}">{{ wm_reply.published_at | timeago }}</time>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ from app import media
|
||||||
from app.models import InboxObject
|
from app.models import InboxObject
|
||||||
from app.models import Webmention
|
from app.models import Webmention
|
||||||
from app.models import WebmentionType
|
from app.models import WebmentionType
|
||||||
from app.utils.url import make_abs
|
|
||||||
from app.utils.datetime import parse_isoformat
|
from app.utils.datetime import parse_isoformat
|
||||||
|
from app.utils.url import make_abs
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -40,7 +40,9 @@ class Face:
|
||||||
return cls(
|
return cls(
|
||||||
ap_actor_id=None,
|
ap_actor_id=None,
|
||||||
url=(
|
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],
|
name=item["properties"]["name"][0],
|
||||||
picture_url=media.resized_media_url(
|
picture_url=media.resized_media_url(
|
||||||
|
@ -95,7 +97,9 @@ def _parse_face(webmention: Webmention, items: list[dict[str, Any]]) -> Face | N
|
||||||
return Face(
|
return Face(
|
||||||
ap_actor_id=None,
|
ap_actor_id=None,
|
||||||
url=(
|
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],
|
name=item["properties"]["name"][0],
|
||||||
picture_url=media.resized_media_url(
|
picture_url=media.resized_media_url(
|
||||||
|
@ -112,6 +116,8 @@ def _parse_face(webmention: Webmention, items: list[dict[str, Any]]) -> Face | N
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WebmentionReply:
|
class WebmentionReply:
|
||||||
|
@ -119,9 +125,10 @@ class WebmentionReply:
|
||||||
content: str
|
content: str
|
||||||
url: str
|
url: str
|
||||||
published_at: datetime.datetime
|
published_at: datetime.datetime
|
||||||
|
in_reply_to: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_webmention(cls, webmention: Webmention) -> "WebmentionReply":
|
def from_webmention(cls, webmention: Webmention) -> Optional["WebmentionReply"]:
|
||||||
if webmention.webmention_type != WebmentionType.REPLY:
|
if webmention.webmention_type != WebmentionType.REPLY:
|
||||||
raise ValueError(f"Unexpected webmention {webmention.id}")
|
raise ValueError(f"Unexpected webmention {webmention.id}")
|
||||||
|
|
||||||
|
@ -143,9 +150,12 @@ class WebmentionReply:
|
||||||
published_at=parse_isoformat(
|
published_at=parse_isoformat(
|
||||||
item["properties"]["published"][0]
|
item["properties"]["published"][0]
|
||||||
).replace(tzinfo=None),
|
).replace(tzinfo=None),
|
||||||
|
in_reply_to=webmention.target, # type: ignore
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
f"Failed to build Face for webmention id={webmention.id}"
|
f"Failed to build Face for webmention id={webmention.id}"
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
Loading…
Reference in New Issue