view libervia/web/pages/_browser/cache.py @ 1618:5d9889f14012 default tip @

server: start major redesign - Add icons to menu items - Switch menu items representation from tuple to dictionary for future extensibility: - Include icon information - Prepare for additional data - Remove "login" from main menu, add login page URL to template data, as it is now a separate right-aligned item
author Goffi <goffi@goffi.org>
date Sat, 26 Oct 2024 23:07:01 +0200
parents 9b451115e726
children
line wrap: on
line source

from browser import window, console as log
from browser.local_storage import storage
from dialog import notification
from bridge import Bridge, AsyncBridge
import json

log.warning = log.warn
session_uuid = window.session_uuid
bridge = Bridge()
async_bridge = AsyncBridge()

# XXX: we don't use browser.object_storage because it is affected by
#   https://github.com/brython-dev/brython/issues/1467 and mixing local_storage.storage
#   and object_storage was resulting in weird behaviour (keys found in one not in the
#   other)


class Cache:

    def __init__(self):
        try:
            cache = storage['libervia_cache']
        except KeyError:
            self.request_data_from_backend()
        else:
            cache = json.loads(cache)
            if cache['metadata']['session_uuid'] != session_uuid:
                log.debug("data in cache are not valid for this session, resetting")
                del storage['libervia_cache']
                self.request_data_from_backend()
            else:
                self._cache = cache
                log.debug("storage cache is used")

    @property
    def roster(self):
        return self._cache['roster']

    @property
    def identities(self):
        return self._cache['identities']

    def update(self):
        log.debug(f"updating: {self._cache}")
        storage['libervia_cache'] = json.dumps(self._cache)
        log.debug("cache stored")

    def _store_if_complete(self):
        self._completed_count -= 1
        if self._completed_count == 0:
            del self._completed_count
            self.update()

    def get_contacts_cb(self, contacts):
        log.debug("roster received")
        roster = self._cache['roster']
        for contact_jid, attributes, groups in contacts:
            roster[contact_jid] = {
                'attributes': attributes,
                'groups': groups,
            }
        self._store_if_complete()

    def identities_base_get_cb(self, identities_raw):
        log.debug("base identities received")
        identities = json.loads(identities_raw)
        self._cache['identities'].update(identities)
        self._store_if_complete()

    def request_failed(self, exc, message):
        notification.show(message.format(exc=exc), "error")
        self._store_if_complete()

    def request_data_from_backend(self):
        self._cache = {
            'metadata': {
                "session_uuid": session_uuid,
            },
            'roster': {},
            'identities': {},
        }
        self._completed_count = 2
        log.debug("requesting roster to backend")
        bridge.contacts_get(
            callback=self.get_contacts_cb,
            errback=lambda e: self.request_failed(e, "Can't get contacts: {exc}")
        )
        log.debug("requesting base identities to backend")
        bridge.identities_base_get(
            callback=self.identities_base_get_cb,
            errback=lambda e: self.request_failed(e, "Can't get base identities: {exc}")
        )

    async def fill_identities(self, entities) -> None:
        """Check that identities for entities exist, request them otherwise"""
        to_get = {e for e in entities if e not in self._cache['identities']}
        if to_get:
            log.debug(f"we don't have all identities in cache, getting {to_get}")
            try:
                new_identities_raw = await async_bridge.identities_get(
                    list(to_get),
                    ['avatar', 'nicknames'],
                )
            except Exception as e:
                notification.show(
                    f"Can't get identities: {e}",
                    "error"
                )
            else:
                new_identities = json.loads(new_identities_raw)
                log.debug(f"new identities: {new_identities.keys()}")
                self._cache['identities'].update(new_identities)
                self.update()
        else:
            # we already have all identities
            log.debug("no missing identity")

    def match_identity(self, entity_jid, text, identity=None):
        """Returns True if a text match an entity identity

        identity will be matching if its jid or any of its name contain text
        @param entity_jid: jid of the entity to check
        @param text: text to use for filtering. Must be in lowercase and stripped
        @param identity: identity data
            if None, it will be retrieved if jid is not matching
        @return: True if entity is matching
        """
        if text in entity_jid:
            return True
        if identity is None:
            try:
                identity = self.identities[entity_jid]
            except KeyError:
                log.debug(f"missing identity: {entity_jid}")
                return False
        return any(text in n.lower() for n in identity['nicknames'])

    def matching_identities(self, text):
        """Return identities corresponding to a text

        """
        text = text.lower().strip()
        for entity_jid, identity in self._cache['identities'].items():
            if ((text in entity_jid
                 or any(text in n.lower() for n in identity['nicknames'])
                 )):
                yield entity_jid


cache = Cache()
roster = cache.roster
identities = cache.identities