diff --git a/app/admin.py b/app/admin.py index b2ef190..b096583 100644 --- a/app/admin.py +++ b/app/admin.py @@ -93,13 +93,20 @@ def get_lookup( def admin_new( request: Request, query: str | None = None, + in_reply_to: str | None = None, db: Session = Depends(get_db), ) -> templates.TemplateResponse: + in_reply_to_object = None + if in_reply_to: + in_reply_to_object = boxes.get_anybox_object_by_ap_id(db, in_reply_to) + if not in_reply_to_object: + raise ValueError(f"Unknown object {in_reply_to=}") + return templates.render_template( db, request, "admin_new.html", - {}, + {"in_reply_to_object": in_reply_to_object}, ) @@ -237,6 +244,7 @@ def admin_actions_new( files: list[UploadFile], content: str = Form(), redirect_url: str = Form(), + in_reply_to: str | None = Form(), csrf_check: None = Depends(verify_csrf_token), db: Session = Depends(get_db), ) -> RedirectResponse: @@ -246,7 +254,12 @@ def admin_actions_new( for f in files: upload = save_upload(db, f) uploads.append((upload, f.filename)) - public_id = boxes.send_create(db, source=content, uploads=uploads) + public_id = boxes.send_create( + db, + source=content, + uploads=uploads, + in_reply_to=in_reply_to or None, + ) return RedirectResponse( request.url_for("outbox_by_public_id", public_id=public_id), status_code=302, diff --git a/app/ap_object.py b/app/ap_object.py index 127320b..13bea2a 100644 --- a/app/ap_object.py +++ b/app/ap_object.py @@ -54,15 +54,14 @@ class Object: @property def context(self) -> str | None: - return self.ap_object.get("context") + return self.ap_object.get("context") or self.ap_object.get("conversation") @property def sensitive(self) -> bool: return self.ap_object.get("sensitive", False) @property - def attachments_old(self) -> list["Attachment"]: - # TODO: set img_src with the proxy URL (proxy_url?) + def attachments(self) -> list["Attachment"]: attachments = [] for obj in self.ap_object.get("attachment", []): proxied_url = _proxied_url(obj["url"]) diff --git a/app/boxes.py b/app/boxes.py index 8df119d..7922070 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -20,10 +20,12 @@ from app.ap_object import RemoteObject from app.config import BASE_URL from app.config import ID from app.database import now -from app.process_outgoing_activities import new_outgoing_activity +from app.outgoing_activities import new_outgoing_activity from app.source import markdownify from app.uploads import upload_to_attachment +AnyboxObject = models.InboxObject | models.OutboxObject + def allocate_outbox_id() -> str: return uuid.uuid4().hex @@ -219,6 +221,7 @@ def send_create( db: Session, source: str, uploads: list[tuple[models.Upload, str]], + in_reply_to: str | None, ) -> str: note_id = allocate_outbox_id() published = now().replace(microsecond=0).isoformat().replace("+00:00", "Z") @@ -226,6 +229,14 @@ def send_create( content, tags = markdownify(db, source) attachments = [] + if in_reply_to: + in_reply_to_object = get_anybox_object_by_ap_id(db, in_reply_to) + if not in_reply_to_object: + raise ValueError(f"Invalid in reply to {in_reply_to=}") + if not in_reply_to_object.context: + raise ValueError("Object has no context") + context = in_reply_to_object.context + for (upload, filename) in uploads: attachments.append(upload_to_attachment(upload, filename)) @@ -243,7 +254,7 @@ def send_create( "url": outbox_object_id(note_id), "tag": tags, "summary": None, - "inReplyTo": None, + "inReplyTo": in_reply_to, "sensitive": False, "attachment": attachments, } @@ -331,6 +342,13 @@ def get_outbox_object_by_ap_id(db: Session, ap_id: str) -> models.OutboxObject | ) +def get_anybox_object_by_ap_id(db: Session, ap_id: str) -> AnyboxObject | None: + if ap_id.startswith(BASE_URL): + return get_outbox_object_by_ap_id(db, ap_id) + else: + return get_inbox_object_by_ap_id(db, ap_id) + + def _handle_delete_activity( db: Session, from_actor: models.Actor, @@ -538,14 +556,18 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None: else None, activity_object_ap_id=ra.activity_object_ap_id, # Hide replies from the stream - is_hidden_from_stream=True if ra.in_reply_to else False, + is_hidden_from_stream=( + True + if (ra.in_reply_to and not ra.in_reply_to.startswith(BASE_URL)) + else False + ), # TODO: handle mentions ) db.add(inbox_object) db.flush() db.refresh(inbox_object) - if ra.ap_type == "Create": + if ra.ap_type == "Note": # TODO: handle create better _handle_create_activity(db, actor, inbox_object) elif ra.ap_type == "Update": pass diff --git a/app/models.py b/app/models.py index 3450565..d2035c8 100644 --- a/app/models.py +++ b/app/models.py @@ -63,6 +63,7 @@ class InboxObject(Base, BaseObject): ap_published_at = Column(DateTime(timezone=True), nullable=False) ap_object: Mapped[ap.RawObject] = Column(JSON, nullable=False) + # Only set for activities activity_object_ap_id = Column(String, nullable=True) visibility = Column(Enum(ap.VisibilityEnum), nullable=False) @@ -242,8 +243,6 @@ class NotificationType(str, enum.Enum): UNDO_LIKE = "undo_like" ANNOUNCE = "announce" UNDO_ANNOUNCE = "undo_announce" - - # TODO: MENTION = "mention" diff --git a/app/process_outgoing_activities.py b/app/outgoing_activities.py similarity index 100% rename from app/process_outgoing_activities.py rename to app/outgoing_activities.py diff --git a/app/templates/admin_new.html b/app/templates/admin_new.html index d3d4e87..bdf30bf 100644 --- a/app/templates/admin_new.html +++ b/app/templates/admin_new.html @@ -2,10 +2,16 @@ {% extends "layout.html" %} {% block content %} +In reply to: +{% if in_reply_to_object %} +{{ utils.display_object(in_reply_to_object) }} +{% endif %} +
diff --git a/app/templates/admin_stream.html b/app/templates/admin_stream.html index 45dce48..233e0ed 100644 --- a/app/templates/admin_stream.html +++ b/app/templates/admin_stream.html @@ -20,6 +20,7 @@ {{ utils.admin_announce_button(inbox_object.ap_id) }} {% endif %} +{{ utils.admin_reply_button(inbox_object.ap_id) }} {% endfor %} {% endblock %} diff --git a/app/templates/layout.html b/app/templates/layout.html index 4d8ef25..e417639 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -15,8 +15,8 @@ {% if is_admin %}