view libervia/web/pages/_browser/jid_search.py @ 1548:66aa6e140ebb

browser: make `jid_search` more generic: rel 423
author Goffi <goffi@goffi.org>
date Wed, 09 Aug 2023 00:22:16 +0200
parents 7f3f5ae7d65a
children e47c24204449
line wrap: on
line source

import json
from urllib.parse import quote, urljoin

from bridge import AsyncBridge as Bridge
from browser import aio, console as log, window
from cache import cache
from template import Template

log.warning = log.warn
profile = window.profile or ""
bridge = Bridge()

class JidSearch:

    def __init__(
        self,
        search_elt,
        container_elt=None,
        filter_cb=None,
        empty_cb=None,
        get_url=None,
        click_cb=None
    ):
        """Initialize the JidSearch instance

        @param search_elt: The HTML <input> element for search
        @param container_elt: The HTML container to display the search results
        @param filter_cb: The callback to filter the search results
        @param empty_cb: The callback when the search box is empty
        @param get_url: The function to get URL for each entity in the search result
        @param click_cb: The function to handle the click event on each entity in the
            search result
        """
        self.search_item_tpl = Template("components/search_item.html")
        self.search_elt = search_elt
        self.search_elt.bind("input", self.on_search_input)
        self.last_query = None
        self.current_query = None
        self.container_elt = container_elt

        if click_cb is not None and get_url is None:
            self.get_url = lambda _: "#"
        else:
            self.get_url = get_url if get_url is not None else self.default_get_url
        self.click_cb = click_cb

        if filter_cb is None:
            if container_elt is None:
                raise ValueError("container_elt must be set if filter_cb is not set")
            filter_cb = self.show_items
        self.filter_cb = filter_cb

        self.empty_cb = empty_cb or self.on_empty_search

        current_search = search_elt.value.strip() or None
        if current_search is not None:
            aio.run(self.perform_search(current_search))

    def default_get_url(self, item):
        """Default method to get the URL for a given entity

        @param item: The item (entity) for which the URL is required
        """
        return urljoin(f"{window.location.href}/", quote(item["entity"]))

    def show_items(self, items):
        """Display the search items in the specified container

        @param items: The list of search items to be displayed
        """
        assert self.container_elt is not None
        self.container_elt.clear()
        for item in items:
            search_item_elt = self.search_item_tpl.get_elt({
                "url": self.get_url(item),
                "item": item,
                "identities": cache.identities
            })
            if self.click_cb is not None:
                search_item_elt.bind('click', lambda evt, item=item: self.click_cb(item))
            self.container_elt <= search_item_elt

    def on_search_input(self, evt):
        """Handle the 'input' event for the search element

        @param evt: The event object
        """
        search_text = evt.target.value.strip()
        if not search_text:
            self.empty_cb()
        elif len(search_text) > 2:
            aio.run(self.perform_search(search_text))

    async def perform_search(self, query):
        """Perform the search operation for a given query

        @param query: The search query
        """
        if self.current_query is None:
            log.debug(f"performing search: {query=}")
            self.current_query = query
            jid_items = json.loads(await bridge.jid_search(query, ""))
            await cache.fill_identities(i["entity"] for i in jid_items)

            self.last_query = query
            self.current_query = None
            current_query = self.search_elt.value.strip()
            if current_query != query:
                await self.perform_search(current_query)
                return
            self.filter_cb(jid_items)

    def on_empty_search(self):
        """Handle the situation when the search box is empty"""
        assert self.container_elt is not None
        items = [
            {
                "entity": jid_,
                "groups": data["groups"]
            }
            for jid_, data in cache.roster.items()
        ]
        self.show_items(items)