feat: add clickable activity cards with comment anchor links to profile tab

Add `url` field to profile activity items so post cards link to `/posts/{slug}` and comment cards link to the parent post with a `#comment-{uid}` anchor, matching notification click behavior. Include the shared `_card_link.html` overlay in the template, fix XP bar rendering for missing `xp` key, and add API + e2e tests verifying the link structure and navigation.
This commit is contained in:
2026-07-04 20:24:59 +00:00
parent c79dfd0291
commit 7002b23eeb
11 changed files with 213 additions and 13 deletions
+97
View File
@@ -170,6 +170,103 @@ def test_missing_profile_returns_404(app_server):
assert r.status_code == 404
def _seed_comment_on_post(commenter_uid):
from datetime import datetime, timezone
from uuid import uuid4
from devplacepy.database import get_table
post_slug, post_uid = _seed_post_seo()
comment_uid = str(uuid4())
get_table("comments").insert(
{
"deleted_at": None,
"deleted_by": None,
"uid": comment_uid,
"target_type": "post",
"target_uid": post_uid,
"post_uid": post_uid,
"user_uid": commenter_uid,
"content": "Activity tab link comment body",
"parent_uid": None,
"created_at": datetime.now(timezone.utc).isoformat(),
}
)
return post_slug, post_uid, comment_uid
def test_activity_comment_exposes_parent_post_url(app_server):
from devplacepy.database import get_table, refresh_snapshot
commenter = _seed_owner()
username = get_table("users").find_one(uid=commenter)["username"]
post_slug, _post_uid, comment_uid = _seed_comment_on_post(commenter)
refresh_snapshot()
r = requests.get(
f"{BASE_URL}/profile/{username}?tab=activity",
headers={"Accept": "application/json"},
)
assert r.status_code == 200
activities = r.json()["activities"]
comment_acts = [a for a in activities if a.get("type") == "comment"]
assert comment_acts, "expected a comment activity"
act = comment_acts[0]
assert act["url"] == f"/posts/{post_slug}#comment-{comment_uid}"
assert act["target_type"] == "post"
def test_activity_comment_url_matches_notification_target(app_server):
from devplacepy.database import get_table, refresh_snapshot, resolve_object_url
commenter = _seed_owner()
username = get_table("users").find_one(uid=commenter)["username"]
_post_slug, post_uid, comment_uid = _seed_comment_on_post(commenter)
refresh_snapshot()
expected = f"{resolve_object_url('post', post_uid)}#comment-{comment_uid}"
r = requests.get(
f"{BASE_URL}/profile/{username}?tab=activity",
headers={"Accept": "application/json"},
)
act = [a for a in r.json()["activities"] if a.get("type") == "comment"][0]
assert act["url"] == expected
def test_activity_post_exposes_url(app_server):
from datetime import datetime, timezone
from devplacepy.database import get_table, refresh_snapshot
owner = _seed_owner()
username = get_table("users").find_one(uid=owner)["username"]
post_slug, post_uid = _seed_post_seo()
get_table("posts").update({"uid": post_uid, "user_uid": owner}, ["uid"])
refresh_snapshot()
r = requests.get(
f"{BASE_URL}/profile/{username}?tab=activity",
headers={"Accept": "application/json"},
)
assert r.status_code == 200
post_acts = [a for a in r.json()["activities"] if a.get("type") == "post"]
assert post_acts, "expected a post activity"
assert post_acts[0]["url"] == f"/posts/{post_slug}"
def test_activity_comment_card_renders_overlay_link(app_server):
from devplacepy.database import get_table, refresh_snapshot
commenter = _seed_owner()
username = get_table("users").find_one(uid=commenter)["username"]
post_slug, _post_uid, comment_uid = _seed_comment_on_post(commenter)
refresh_snapshot()
r = requests.get(f"{BASE_URL}/profile/{username}?tab=activity")
assert r.status_code == 200
assert "activity-card card-link-host" in r.text
assert f'class="card-link" href="/posts/{post_slug}#comment-{comment_uid}"' in r.text
def test_viewing_profile_marks_notification_read(app_server):
import time
from datetime import datetime, timezone