feat: direct message (DM) receive and reply support

- Detect incoming DM visibility in inbox listener by checking absence of
  the public collection URL in object.toIds/ccIds; store isDirect and
  senderActorUrl on mention notifications
- Add native AP reply path in compose controller: when is-direct=true,
  build Create(Note) addressed only to the sender and deliver via
  ctx.sendActivity() instead of posting a public Micropub blog reply
- Add dedicated "Direct" tab to notifications view (separate from Replies)
  with its own count; update storage query so mention tab filters only
  mention type, reply tab filters only reply type
- Show lock badge (🔒) on direct mention notification cards and add
  ap-notification--direct CSS class
- Compose view: show DM notice banner, hide syndication targets, and
  change submit label when replying to a direct message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
svemagie
2026-03-13 06:32:50 +01:00
parent 1c2fb321bc
commit ea9a9856e9
7 changed files with 156 additions and 14 deletions

View File

@@ -1,6 +1,6 @@
{# Notification card partial #}
<div class="ap-notification{% if not item.read %} ap-notification--unread{% endif %}">
<div class="ap-notification{% if not item.read %} ap-notification--unread{% endif %}{% if item.isDirect %} ap-notification--direct{% endif %}">
{# Dismiss button #}
<form method="post" action="{{ mountPath }}/admin/reader/notifications/delete" class="ap-notification__dismiss">
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
@@ -18,7 +18,7 @@
<span class="ap-notification__avatar ap-notification__avatar--default" aria-hidden="true">{{ item.actorName[0] | upper if item.actorName else "?" }}</span>
{% endif %}
<span class="ap-notification__type-badge">
{% if item.type == "like" %}❤{% elif item.type == "boost" %}🔁{% elif item.type == "follow" %}👤{% elif item.type == "reply" %}💬{% elif item.type == "mention" %}@{% endif %}
{% if item.type == "like" %}❤{% elif item.type == "boost" %}🔁{% elif item.type == "follow" %}👤{% elif item.type == "reply" %}💬{% elif item.type == "mention" %}{% if item.isDirect %}🔒{% else %}@{% endif %}{% endif %}
</span>
</div>