docs: add block/mute user relations, emoji-sync CLI, and uid indexes
DevPlace CI / test (push) Failing after 2m7s

- Add `/block`, `/mute` endpoints with block/unblock and mute/unmute functionality in `routers/relations.py`, hiding blocked users' content everywhere except their own profile while muting only suppresses notifications
- Introduce `devplace emoji-sync` CLI command to regenerate `static/js/emoji-shortcodes.js` from the emoji library, documented in `CLAUDE.md` and wired in `cli.py`
- Create `get_blocked_uids()` database helper and apply it in `content.py` `load_detail()` to filter blocked users' posts from detail views
- Implement `_uid_index()` and `_drop_index()` helpers in `database.py` for unique uid indexes across tables, with `user_relations` added to `SOFT_DELETE_TABLES`
- Document new routes in `AGENTS.md` and `README.md`, including emoji shortcodes rendering behavior distinct from the emoji picker
This commit is contained in:
2026-06-19 08:06:09 +00:00
parent f3a4667fce
commit 741d7aade6
136 changed files with 3025 additions and 564 deletions
+116
View File
@@ -259,3 +259,119 @@ def test_interleave_by_author_single_author_keeps_order():
rows = [{"uid": i, "user_uid": "a"} for i in range(4)]
spread = interleave_by_author(rows)
assert [r["uid"] for r in spread] == [0, 1, 2, 3]
def _relation(actor_uid, target_uid, kind):
get_table("user_relations").insert(
{
"uid": generate_uid(),
"user_uid": actor_uid,
"target_uid": target_uid,
"kind": kind,
"created_at": _now_db_helpers(),
"deleted_at": None,
"deleted_by": None,
}
)
def test_get_user_relations_partitions_both_kinds(local_db):
from devplacepy.database import get_user_relations, invalidate_user_relations
actor = _user_db_helpers()
blocked = _user_db_helpers()
muted = _user_db_helpers()
_relation(actor, blocked, "block")
_relation(actor, muted, "mute")
invalidate_user_relations(actor)
relations = get_user_relations(actor)
assert relations["block"] == frozenset({blocked})
assert relations["mute"] == frozenset({muted})
def test_get_user_relations_anonymous_is_empty(local_db):
from devplacepy.database import get_user_relations
relations = get_user_relations(None)
assert relations["block"] == frozenset()
assert relations["mute"] == frozenset()
def test_get_silenced_uids_is_union(local_db):
from devplacepy.database import get_silenced_uids, invalidate_user_relations
actor = _user_db_helpers()
blocked = _user_db_helpers()
muted = _user_db_helpers()
_relation(actor, blocked, "block")
_relation(actor, muted, "mute")
invalidate_user_relations(actor)
assert get_silenced_uids(actor) == frozenset({blocked, muted})
def test_soft_deleted_relation_is_ignored(local_db):
from devplacepy.database import get_blocked_uids, invalidate_user_relations
actor = _user_db_helpers()
target = _user_db_helpers()
_relation(actor, target, "block")
invalidate_user_relations(actor)
assert get_blocked_uids(actor) == frozenset({target})
row = get_table("user_relations").find_one(user_uid=actor, target_uid=target)
get_table("user_relations").update(
{"id": row["id"], "deleted_at": _now_db_helpers()}, ["id"]
)
invalidate_user_relations(actor)
assert get_blocked_uids(actor) == frozenset()
def test_paginate_excludes_blocked_authors(local_db):
from devplacepy.database import paginate, invalidate_user_relations
viewer = _user_db_helpers()
blocked = _user_db_helpers()
visible = _user_db_helpers()
blocked_post = _post(blocked)
visible_post = _post(visible)
_relation(viewer, blocked, "block")
invalidate_user_relations(viewer)
rows, _ = paginate(get_table("posts"), viewer_uid=viewer)
uids = {r["uid"] for r in rows}
assert visible_post in uids
assert blocked_post not in uids
def test_paginate_without_viewer_keeps_all(local_db):
from devplacepy.database import paginate, invalidate_user_relations
viewer = _user_db_helpers()
blocked = _user_db_helpers()
blocked_post = _post(blocked)
_relation(viewer, blocked, "block")
invalidate_user_relations(viewer)
rows, _ = paginate(get_table("posts"), viewer_uid=None)
assert blocked_post in {r["uid"] for r in rows}
def test_load_comments_drops_blocked_author(local_db):
from devplacepy.database import load_comments, invalidate_user_relations
viewer = _user_db_helpers()
host = _user_db_helpers()
blocked = _user_db_helpers()
post_uid = _post(host)
_comment(blocked, post_uid)
_relation(viewer, blocked, "block")
invalidate_user_relations(viewer)
viewer_row = get_table("users").find_one(uid=viewer)
comments = load_comments("post", post_uid, viewer_row)
assert comments == []
others = load_comments("post", post_uid, None)
assert len(others) == 1