From 87ce2d497090cd8f350d8e9fb4d8891c6da66c01 Mon Sep 17 00:00:00 2001 From: giles Date: Mon, 23 Feb 2026 08:18:59 +0000 Subject: [PATCH] Add actor search with infinite scroll Replace single WebFinger lookup with paginated search across cached remote actors and local profiles. New _search_results.html partial with htmx infinite scroll sentinel. Form submits via hx-get for seamless pagination. Co-Authored-By: Claude Opus 4.6 --- bp/social/routes.py | 43 ++++++++++++++-- shared | 2 +- templates/federation/_search_results.html | 61 +++++++++++++++++++++++ templates/federation/search.html | 19 ++++--- 4 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 templates/federation/_search_results.html diff --git a/bp/social/routes.py b/bp/social/routes.py index 04c0d99..7878156 100644 --- a/bp/social/routes.py +++ b/bp/social/routes.py @@ -138,13 +138,50 @@ def register(url_prefix="/social"): async def search(): actor = getattr(g, "_social_actor", None) query = request.args.get("q", "").strip() - result = None + actors = [] + total = 0 + followed_urls: set[str] = set() if query: - result = await services.federation.search_remote_actor(g.s, query) + actors, total = await services.federation.search_actors(g.s, query) + if actor: + following, _ = await services.federation.get_following( + g.s, actor.preferred_username, page=1, per_page=1000, + ) + followed_urls = {a.actor_url for a in following} return await render_template( "federation/search.html", query=query, - result=result, + actors=actors, + total=total, + page=1, + followed_urls=followed_urls, + actor=actor, + ) + + @bp.get("/search/page") + async def search_page(): + actor = getattr(g, "_social_actor", None) + query = request.args.get("q", "").strip() + page = request.args.get("page", 1, type=int) + actors = [] + total = 0 + followed_urls: set[str] = set() + if query: + actors, total = await services.federation.search_actors( + g.s, query, page=page, + ) + if actor: + following, _ = await services.federation.get_following( + g.s, actor.preferred_username, page=1, per_page=1000, + ) + followed_urls = {a.actor_url for a in following} + return await render_template( + "federation/_search_results.html", + actors=actors, + total=total, + page=page, + query=query, + followed_urls=followed_urls, actor=actor, ) diff --git a/shared b/shared index b16ba34..f085d4a 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit b16ba34b40291a71a546130862acc00959c225fd +Subproject commit f085d4a8d04c70b50330a743c1cfd50ff5d6a81c diff --git a/templates/federation/_search_results.html b/templates/federation/_search_results.html new file mode 100644 index 0000000..ca8c248 --- /dev/null +++ b/templates/federation/_search_results.html @@ -0,0 +1,61 @@ +{% for a in actors %} +
+ {% if a.icon_url %} + + {% else %} +
+ {{ (a.display_name or a.preferred_username)[0] | upper }} +
+ {% endif %} + +
+ {% if a.id %} + + {{ a.display_name or a.preferred_username }} + + {% else %} + {{ a.display_name or a.preferred_username }} + {% endif %} +
@{{ a.preferred_username }}@{{ a.domain }}
+ {% if a.summary %} +
{{ a.summary | striptags }}
+ {% endif %} +
+ + {% if actor %} +
+ {% if a.actor_url in (followed_urls or []) %} +
+ + + +
+ {% else %} +
+ + + +
+ {% endif %} +
+ {% endif %} +
+{% endfor %} + +{% if actors | length >= 20 %} +
+
+{% endif %} diff --git a/templates/federation/search.html b/templates/federation/search.html index a294fff..62c33dc 100644 --- a/templates/federation/search.html +++ b/templates/federation/search.html @@ -5,11 +5,14 @@ {% block social_content %}

Search

-
+
+ placeholder="Search users or @user@instance.tld">
-{% if query and not result %} -

No results found for {{ query }}

+{% if query and total %} +

{{ total }} result{{ 's' if total != 1 }} for {{ query }}

+{% elif query %} +

No results found for {{ query }}

{% endif %} -{% if result %} - {% include "federation/actor_card.html" %} -{% endif %} +
+ {% include "federation/_search_results.html" %} +
{% endblock %}