488
|
1 #!/usr/bin/env python3 |
|
2 |
|
3 #desktop/mobile frontend for Libervia XMPP client |
|
4 # Copyright (C) 2016-2022 Jérôme Poisson (goffi@goffi.org) |
|
5 |
|
6 # This program is free software: you can redistribute it and/or modify |
|
7 # it under the terms of the GNU Affero General Public License as published by |
|
8 # the Free Software Foundation, either version 3 of the License, or |
|
9 # (at your option) any later version. |
|
10 |
|
11 # This program is distributed in the hope that it will be useful, |
|
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 # GNU Affero General Public License for more details. |
|
15 |
|
16 # You should have received a copy of the GNU Affero General Public License |
|
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
19 |
|
20 from functools import partial |
|
21 import json |
|
22 from typing import Any, Dict, Optional |
|
23 |
|
24 from kivy import properties |
|
25 from kivy.metrics import sp |
|
26 from kivy.uix.behaviors import ButtonBehavior |
|
27 from kivy.uix.boxlayout import BoxLayout |
|
28 from sat.core import log as logging |
|
29 from sat.core.i18n import _ |
|
30 from sat.tools.common import data_format |
|
31 from sat_frontends.bridge.bridge_frontend import BridgeException |
|
32 from sat_frontends.quick_frontend import quick_widgets |
|
33 from sat_frontends.tools import jid |
|
34 |
|
35 from cagou import G |
|
36 from cagou.core.menu import SideMenu |
|
37 |
|
38 from ..core import cagou_widget |
|
39 from ..core.common import SymbolButton |
|
40 from ..core.constants import Const as C |
|
41 from ..core.image import Image |
|
42 |
|
43 log = logging.getLogger(__name__) |
|
44 |
|
45 PLUGIN_INFO = { |
|
46 "name": _("blog"), |
|
47 "main": "Blog", |
|
48 "description": _("(micro)blog"), |
|
49 "icon_symbol": "pencil", |
|
50 } |
|
51 |
|
52 |
|
53 class SearchButton(SymbolButton): |
|
54 blog = properties.ObjectProperty() |
|
55 |
|
56 def on_release(self, *args): |
|
57 self.blog.header_input.dispatch('on_text_validate') |
|
58 |
|
59 |
|
60 class NewPostButton(SymbolButton): |
|
61 blog = properties.ObjectProperty() |
|
62 |
|
63 def on_release(self, *args): |
|
64 self.blog.show_new_post_menu() |
|
65 |
|
66 |
|
67 class NewPosttMenu(SideMenu): |
|
68 blog = properties.ObjectProperty() |
|
69 size_hint_close = (1, 0) |
|
70 size_hint_open = (1, 0.9) |
|
71 |
|
72 def _publish_cb(self, item_id: str) -> None: |
|
73 G.host.addNote( |
|
74 _("blog post published"), |
|
75 _("your blog post has been published with ID {item_id}").format( |
|
76 item_id=item_id |
|
77 ) |
|
78 ) |
|
79 self.blog.load_blog() |
|
80 |
|
81 def _publish_eb(self, exc: BridgeException) -> None: |
|
82 G.host.addNote( |
|
83 _("Problem while publish blog post"), |
|
84 _("Can't publish blog post at {node!r} from {service}: {problem}").format( |
|
85 node=self.blog.node or G.host.ns_map.get("microblog"), |
|
86 service=( |
|
87 self.blog.service if self.blog.service |
|
88 else G.host.profiles[self.blog.profile].whoami, |
|
89 ), |
|
90 problem=exc |
|
91 ), |
|
92 C.XMLUI_DATA_LVL_ERROR |
|
93 ) |
|
94 |
|
95 def publish( |
|
96 self, |
|
97 title: str, |
|
98 content: str, |
|
99 e2ee: bool = False |
|
100 ) -> None: |
|
101 self.hide() |
|
102 mb_data: Dict[str, Any] = {"content_rich": content} |
|
103 if e2ee: |
|
104 mb_data["encrypted"] = True |
|
105 title = title.strip() |
|
106 if title: |
|
107 mb_data["title_rich"] = title |
|
108 G.host.bridge.mbSend( |
|
109 self.blog.service, |
|
110 self.blog.node, |
|
111 data_format.serialise(mb_data), |
|
112 self.blog.profile, |
|
113 callback=self._publish_cb, |
|
114 errback=self._publish_eb, |
|
115 ) |
|
116 |
|
117 |
|
118 class BlogPostAvatar(ButtonBehavior, Image): |
|
119 pass |
|
120 |
|
121 |
|
122 class BlogPostWidget(BoxLayout): |
|
123 blog_data = properties.DictProperty() |
|
124 font_size = properties.NumericProperty(sp(12)) |
|
125 title_font_size = properties.NumericProperty(sp(14)) |
|
126 |
|
127 |
|
128 class Blog(quick_widgets.QuickWidget, cagou_widget.CagouWidget): |
|
129 posts_widget = properties.ObjectProperty() |
|
130 service = properties.StringProperty() |
|
131 node = properties.StringProperty() |
|
132 use_header_input = True |
|
133 |
|
134 def __init__(self, host, target, profiles): |
|
135 quick_widgets.QuickWidget.__init__(self, G.host, target, profiles) |
|
136 cagou_widget.CagouWidget.__init__(self) |
|
137 search_btn = SearchButton(blog=self) |
|
138 self.headerInputAddExtra(search_btn) |
|
139 new_post_btn = NewPostButton(blog=self) |
|
140 self.headerInputAddExtra(new_post_btn) |
|
141 self.load_blog() |
|
142 |
|
143 def on_kv_post(self, __): |
|
144 self.bind( |
|
145 service=lambda __, value: self.load_blog(), |
|
146 node=lambda __, value: self.load_blog(), |
|
147 ) |
|
148 |
|
149 def onHeaderInput(self): |
|
150 text = self.header_input.text.strip() |
|
151 # for now we only use text as node |
|
152 self.node = text |
|
153 |
|
154 def show_new_post_menu(self): |
|
155 """Show the "add a contact" menu""" |
|
156 NewPosttMenu(blog=self).show() |
|
157 |
|
158 def _mb_get_cb(self, blog_data_s: str) -> None: |
|
159 blog_data = json.loads(blog_data_s) |
|
160 for item in blog_data["items"]: |
|
161 self.posts_widget.add_widget(BlogPostWidget(blog_data=item)) |
|
162 |
|
163 def _mb_get_eb( |
|
164 self, |
|
165 exc: BridgeException, |
|
166 ) -> None: |
|
167 G.host.addNote( |
|
168 _("Problem while getting blog data"), |
|
169 _("Can't get blog for {node!r} at {service}: {problem}").format( |
|
170 node=self.node or G.host.ns_map.get("microblog"), |
|
171 service=self.service if self.service else G.host.profiles[self.profile].whoami, |
|
172 problem=exc |
|
173 ), |
|
174 C.XMLUI_DATA_LVL_ERROR |
|
175 ) |
|
176 |
|
177 def load_blog( |
|
178 self, |
|
179 ) -> None: |
|
180 """Retrieve a blog and display it""" |
|
181 extra = {} |
|
182 self.posts_widget.clear_widgets() |
|
183 G.host.bridge.mbGet( |
|
184 self.service, |
|
185 self.node, |
|
186 20, |
|
187 [], |
|
188 data_format.serialise(extra), |
|
189 self.profile, |
|
190 callback=self._mb_get_cb, |
|
191 errback=self._mb_get_eb, |
|
192 ) |