view libervia/desktop_kivy/plugins/plugin_wid_blog.py @ 506:0480f883f0a6

plugin calls: update UI: - there is now a "search" UI to select a contact to call - "call" UI is displayed only when we actually are in a call - new control button to (un)mute audio and video - new control button to go to fullscreen/back to normal - add an extra "hang up" button directly in the call UI, so there is always one even in fullscreen mode - UI is similar to the one implemented in web frontend - notification + ringtone + desktop notification on incoming call - if an incoming call is cancelled from initiator, confirmation dialog is removed rel 425
author Goffi <goffi@goffi.org>
date Wed, 25 Oct 2023 15:28:44 +0200
parents b3cedbee561d
children
line wrap: on
line source

#!/usr/bin/env python3

#desktop/mobile frontend for Libervia XMPP client
# Copyright (C) 2016-2022 Jérôme Poisson (goffi@goffi.org)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from functools import partial
import json
from typing import Any, Dict, Optional

from kivy import properties
from kivy.metrics import sp
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from libervia.backend.core import log as logging
from libervia.backend.core.i18n import _
from libervia.backend.tools.common import data_format
from libervia.frontends.bridge.bridge_frontend import BridgeException
from libervia.frontends.quick_frontend import quick_widgets
from libervia.frontends.tools import jid

from libervia.desktop_kivy import G
from libervia.desktop_kivy.core.menu import SideMenu

from ..core import cagou_widget
from ..core.common import SymbolButton
from ..core.constants import Const as C
from ..core.image import Image

log = logging.getLogger(__name__)

PLUGIN_INFO = {
    "name": _("blog"),
    "main": "Blog",
    "description": _("(micro)blog"),
    "icon_symbol": "pencil",
}


class SearchButton(SymbolButton):
    blog = properties.ObjectProperty()

    def on_release(self, *args):
        self.blog.header_input.dispatch('on_text_validate')


class NewPostButton(SymbolButton):
    blog = properties.ObjectProperty()

    def on_release(self, *args):
        self.blog.show_new_post_menu()


class NewPosttMenu(SideMenu):
    blog = properties.ObjectProperty()
    size_hint_close = (1, 0)
    size_hint_open = (1, 0.9)

    def _publish_cb(self, item_id: str) -> None:
        G.host.add_note(
            _("blog post published"),
            _("your blog post has been published with ID {item_id}").format(
                item_id=item_id
            )
        )
        self.blog.load_blog()

    def _publish_eb(self, exc: BridgeException) -> None:
        G.host.add_note(
            _("Problem while publish blog post"),
            _("Can't publish blog post at {node!r} from {service}: {problem}").format(
                node=self.blog.node or G.host.ns_map.get("microblog"),
                service=(
                    self.blog.service if self.blog.service
                    else G.host.profiles[self.blog.profile].whoami,
                ),
                problem=exc
            ),
            C.XMLUI_DATA_LVL_ERROR
        )

    def publish(
            self,
            title: str,
            content: str,
            e2ee: bool = False
    ) -> None:
        self.hide()
        mb_data: Dict[str, Any] = {"content_rich": content}
        if e2ee:
            mb_data["encrypted"] = True
        title = title.strip()
        if title:
            mb_data["title_rich"] = title
        G.host.bridge.mb_send(
            self.blog.service,
            self.blog.node,
            data_format.serialise(mb_data),
            self.blog.profile,
            callback=self._publish_cb,
            errback=self._publish_eb,
        )


class BlogPostAvatar(ButtonBehavior, Image):
    pass


class BlogPostWidget(BoxLayout):
    blog_data = properties.DictProperty()
    font_size = properties.NumericProperty(sp(12))
    title_font_size = properties.NumericProperty(sp(14))


class Blog(quick_widgets.QuickWidget, cagou_widget.LiberviaDesktopKivyWidget):
    posts_widget = properties.ObjectProperty()
    service = properties.StringProperty()
    node = properties.StringProperty()
    use_header_input = True

    def __init__(self, host, target, profiles):
        quick_widgets.QuickWidget.__init__(self, G.host, target, profiles)
        cagou_widget.LiberviaDesktopKivyWidget.__init__(self)
        search_btn = SearchButton(blog=self)
        self.header_input_add_extra(search_btn)
        new_post_btn = NewPostButton(blog=self)
        self.header_input_add_extra(new_post_btn)
        self.load_blog()

    def on_kv_post(self, __):
        self.bind(
            service=lambda __, value: self.load_blog(),
            node=lambda __, value: self.load_blog(),
        )

    def on_header_wid_input(self):
        text = self.header_input.text.strip()
        # for now we only use text as node
        self.node = text

    def show_new_post_menu(self):
        """Show the "add a contact" menu"""
        NewPosttMenu(blog=self).show()

    def _mb_get_cb(self, blog_data_s: str) -> None:
        blog_data = json.loads(blog_data_s)
        for item in blog_data["items"]:
            self.posts_widget.add_widget(BlogPostWidget(blog_data=item))

    def _mb_get_eb(
        self,
        exc: BridgeException,
    ) -> None:
        G.host.add_note(
            _("Problem while getting blog data"),
            _("Can't get blog for {node!r} at {service}: {problem}").format(
                node=self.node or G.host.ns_map.get("microblog"),
                service=self.service if self.service else G.host.profiles[self.profile].whoami,
                problem=exc
            ),
            C.XMLUI_DATA_LVL_ERROR
        )

    def load_blog(
        self,
    ) -> None:
        """Retrieve a blog and display it"""
        extra = {}
        self.posts_widget.clear_widgets()
        G.host.bridge.mb_get(
            self.service,
            self.node,
            20,
            [],
            data_format.serialise(extra),
            self.profile,
            callback=self._mb_get_cb,
            errback=self._mb_get_eb,
        )