import time import RNS from kivy.metrics import dp,sp from kivy.uix.boxlayout import BoxLayout from kivy.properties import StringProperty, BooleanProperty from kivymd.uix.list import MDList, IconLeftWidget, IconRightWidget, TwoLineAvatarIconListItem from kivymd.uix.menu import MDDropdownMenu from kivy.uix.gridlayout import GridLayout from kivy.uix.boxlayout import BoxLayout from kivy.clock import Clock from kivy.core.clipboard import Clipboard from kivymd.uix.button import MDRectangleFlatButton from kivymd.uix.dialog import MDDialog from kivy.lang.builder import Builder from kivy.utils import escape_markup if RNS.vendor.platformutils.get_platform() == "android": from ui.helpers import multilingual_markup, sig_icon_for_q else: from .helpers import multilingual_markup, sig_icon_for_q if RNS.vendor.platformutils.get_platform() == "android": from ui.helpers import ts_format else: from .helpers import ts_format class Announces(): def __init__(self, app): self.app = app self.context_dests = [] self.added_item_dests = [] self.list = None if not self.app.root.ids.screen_manager.has_screen("announces_screen"): self.screen = Builder.load_string(layout_announces_screen) self.screen.app = self.app self.ids = self.screen.ids self.app.root.ids.screen_manager.add_widget(self.screen) self.fetch_announces() self.list = MDList() # self.update() def fetch_announces(self): self.announces = self.app.sideband.list_announces() def reload(self): self.clear_list() self.update() def clear_list(self): if self.list != None: self.list.clear_widgets() self.context_dests = [] self.added_item_dests = [] def update(self): us = time.time() self.fetch_announces() self.update_widget() self.app.sideband.setstate("app.flags.new_announces", False) RNS.log("Updated announce stream widgets in "+RNS.prettytime(time.time()-us), RNS.LOG_DEBUG) def update_widget(self): if self.list == None: self.list = MDList() remove_widgets = [] for item in self.list.children: if not item.sb_uid in (a["dest"] for a in self.announces): remove_widgets.append(item) else: for announce in self.announces: if announce["dest"] == item.sb_uid: if announce["time"] > item.ts: remove_widgets.append(item) break for item in remove_widgets: if item.sb_uid in self.added_item_dests: self.added_item_dests.remove(item.sb_uid) self.list.remove_widget(item) for announce in self.announces: context_dest = announce["dest"] ts = announce["time"] a_name = announce["name"] a_cost = announce["cost"] dest_type = announce["type"] a_rssi = None a_snr = None a_q = None link_extras_str = "" link_extras_full = "" if "extras" in announce and announce["extras"] != None: extras = announce["extras"] if "link_stats" in extras: link_stats = extras["link_stats"] if "rssi" in link_stats and "snr" in link_stats and "q" in link_stats: a_rssi = link_stats["rssi"] a_snr = link_stats["snr"] a_q = link_stats["q"] if a_rssi != None and a_snr != None and a_q != None: link_extras_str = f" ([b]RSSI[/b] {a_rssi} [b]SNR[/b] {a_snr})" link_extras_full = f"\n[b]Link Quality[/b] {a_q}%[/b]\n[b]RSSI[/b] {a_rssi}\n[b]SNR[/b] {a_snr}" sig_icon = multilingual_markup(sig_icon_for_q(a_q).encode("utf-8")).decode("utf-8") if not context_dest in self.added_item_dests: if self.app.sideband.is_trusted(context_dest): trust_icon = "account-check" else: trust_icon = "account-question" def gen_info(ts, dest, name, cost, dtype, link_extras): name = multilingual_markup(escape_markup(str(name)).encode("utf-8")).decode("utf-8") cost = str(cost) def x(sender): yes_button = MDRectangleFlatButton(text="OK",font_size=dp(18)) if dtype == "lxmf.delivery": ad_text = "[size=22dp]LXMF Peer[/size]\n\n[b]Received[/b] "+ts+"\n[b]Address[/b] "+RNS.prettyhexrep(dest)+"\n[b]Name[/b] "+name+"\n[b]Stamp Cost[/b] "+cost+link_extras if dtype == "lxmf.propagation": ad_text = "[size=22dp]LXMF Propagation Node[/size]\n\n[b]Received[/b] "+ts+"\n[b]Address[/b] "+RNS.prettyhexrep(dest)+link_extras dialog = MDDialog( text=ad_text, buttons=[ yes_button ], # elevation=0, ) def dl_yes(s): dialog.dismiss() yes_button.bind(on_release=dl_yes) item.dmenu.dismiss() dialog.open() return x time_string = sig_icon + " " + time.strftime(ts_format, time.localtime(ts)) + link_extras_str time_string_plain = time.strftime(ts_format, time.localtime(ts)) if dest_type == "lxmf.delivery": disp_name = multilingual_markup(escape_markup(str(self.app.sideband.peer_display_name(context_dest))).encode("utf-8")).decode("utf-8") iconl = IconLeftWidget(icon=trust_icon) elif dest_type == "lxmf.propagation": disp_name = "Propagation Node "+RNS.prettyhexrep(context_dest) iconl = IconLeftWidget(icon="upload-network") else: disp_name = "Unknown Announce" iconl = IconLeftWidget(icon="progress-question") item = TwoLineAvatarIconListItem(text=time_string, secondary_text=disp_name, on_release=gen_info(time_string_plain, context_dest, a_name, a_cost, dest_type, link_extras_full)) item.add_widget(iconl) item.sb_uid = context_dest item.ts = ts def gen_del(dest, item): def x(): yes_button = MDRectangleFlatButton(text="Yes",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_reject, text_color=self.app.color_reject) no_button = MDRectangleFlatButton(text="No",font_size=dp(18)) dialog = MDDialog( title="Delete announce?", buttons=[ yes_button, no_button ], padding=[0,0,dp(32),0] # elevation=0, ) def dl_yes(s): dialog.dismiss() def cb(dt): self.app.sideband.delete_announce(dest) self.update() Clock.schedule_once(cb, 0.2) def dl_no(s): dialog.dismiss() yes_button.bind(on_release=dl_yes) no_button.bind(on_release=dl_no) item.dmenu.dismiss() dialog.open() return x def gen_conv(dest, item): def x(): item.dmenu.dismiss() self.app.conversation_from_announce_action(dest) return x def gen_copy_addr(dest, item): def x(): Clipboard.copy(RNS.hexrep(dest, delimit=False)) item.dmenu.dismiss() return x def gen_set_node(dest, item): def x(): item.dmenu.dismiss() self.app.sideband.set_active_propagation_node(dest) self.app.sideband.config["lxmf_propagation_node"] = dest self.app.sideband.save_configuration() return x if dest_type == "lxmf.delivery": dm_items = [ { "viewclass": "OneLineListItem", "text": "Converse", "height": dp(40), "on_release": gen_conv(context_dest, item) }, { "viewclass": "OneLineListItem", "text": "Copy address", "height": dp(40), "on_release": gen_copy_addr(context_dest, item) }, { "text": "Delete Announce", "viewclass": "OneLineListItem", "height": dp(40), "on_release": gen_del(context_dest, item) } ] elif dest_type == "lxmf.propagation": dm_items = [ { "viewclass": "OneLineListItem", "text": "Use this Propagation Node", "height": dp(40), "on_release": gen_set_node(context_dest, item) }, { "viewclass": "OneLineListItem", "text": "Copy address", "height": dp(40), "on_release": gen_copy_addr(context_dest, item) }, { "text": "Delete Announce", "viewclass": "OneLineListItem", "height": dp(40), "on_release": gen_del(context_dest, item) } ] else: dm_items = [] item.iconr = IconRightWidget(icon="dots-vertical"); item.dmenu = MDDropdownMenu( caller=item.iconr, items=dm_items, position="auto", width=dp(256), elevation=0, radius=dp(3), ) item.dmenu.md_bg_color = self.app.color_hover def callback_factory(ref): def x(sender): ref.dmenu.open() return x item.iconr.bind(on_release=callback_factory(item)) item.add_widget(item.iconr) self.added_item_dests.append(context_dest) self.list.add_widget(item, index=len(self.list.children)) def get_widget(self): return self.list layout_announces_screen = """ MDScreen: name: "announces_screen" BoxLayout: orientation: "vertical" MDTopAppBar: title: "Announce Stream" anchor_title: "left" elevation: 0 left_action_items: [['menu', lambda x: root.app.nav_drawer.set_state("open")]] right_action_items: [ ['close', lambda x: root.app.close_settings_action(self)], ] # [['eye-off', lambda x: root.ids.screen_manager.app.announce_filter_action(self)]] ScrollView: id: announces_scrollview MDBoxLayout: orientation: "vertical" spacing: "24dp" size_hint_y: None height: self.minimum_height padding: dp(64) MDLabel: id: announces_info markup: True text: "" size_hint_y: None text_size: self.width, None height: self.texture_size[1] """