From 132e02685be51fa72eb2c572e7ccad4afcc98ef4 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 31 May 2024 23:35:42 +0200 Subject: [PATCH] Added objects view. Added ability to display icon styles from everyone. --- sbapp/main.py | 60 ++++++++++++++++++++++----- sbapp/sideband/core.py | 76 ++++++++++++++++++++++++++++++---- sbapp/ui/conversations.py | 86 ++++++++++++++++++++++++++++++++++----- sbapp/ui/layouts.py | 27 +++++++++++- sbapp/ui/objectdetails.py | 11 ++++- 5 files changed, 227 insertions(+), 33 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 5b8a4b0..2ef0e76 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -199,6 +199,8 @@ class SidebandApp(MDApp): self.dark_theme_text_color = dark_theme_text_color self.conversations_view = None + self.include_conversations = True + self.include_objects = False self.messages_view = None self.map = None self.map_layer = None @@ -1158,10 +1160,15 @@ class SidebandApp(MDApp): def open_conversation(self, context_dest, direction="left"): self.outbound_mode_paper = False self.outbound_mode_command = False - if self.sideband.config["propagation_by_default"]: - self.outbound_mode_propagation = True + self.outbound_mode_propagation = False + if self.include_objects and not self.include_conversations: + if self.sideband.config["propagation_by_default"]: + self.outbound_mode_propagation = True + else: + self.outbound_mode_command = True else: - self.outbound_mode_propagation = False + if self.sideband.config["propagation_by_default"]: + self.outbound_mode_propagation = True self.root.ids.screen_manager.transition.direction = direction self.messages_view = Messages(self, context_dest) @@ -1570,6 +1577,28 @@ class SidebandApp(MDApp): ### Conversations screen ###################################### def conversations_action(self, sender=None, direction="left", no_transition=False): + if self.include_objects: + self.include_conversations = True + self.include_objects = False + self.conversations_view.update() + + if no_transition: + self.root.ids.screen_manager.transition = self.no_transition + else: + self.root.ids.screen_manager.transition = self.slide_transition + self.root.ids.screen_manager.transition.direction = direction + + self.open_conversations(direction=direction) + + if no_transition: + self.root.ids.screen_manager.transition = self.slide_transition + + def objects_action(self, sender=None, direction="left", no_transition=False): + if self.include_conversations: + self.include_conversations = False + self.include_objects = True + self.conversations_view.update() + if no_transition: self.root.ids.screen_manager.transition = self.no_transition else: @@ -2016,8 +2045,6 @@ class SidebandApp(MDApp): if sender != self.settings_screen.ids.settings_lang_hebrew: self.settings_screen.ids.settings_lang_hebrew.active = False - RNS.log("Sender: "+str(sender)) - if self.settings_screen.ids.settings_lang_default.active: self.sideband.config["input_language"] = None self.settings_screen.ids.settings_display_name.font_name = "" @@ -2059,6 +2086,11 @@ class SidebandApp(MDApp): self.sideband.save_configuration() self.sideband.setstate("wants.viewupdate.conversations", True) + def save_display_style_from_trusted_only(sender=None, event=None): + self.sideband.config["display_style_from_all"] = not self.settings_screen.ids.display_style_from_trusted_only.active + self.sideband.save_configuration() + self.sideband.setstate("wants.viewupdate.conversations", True) + def save_advanced_stats(sender=None, event=None): self.sideband.config["advanced_stats"] = self.settings_screen.ids.settings_advanced_statistics.active self.sideband.save_configuration() @@ -2175,6 +2207,9 @@ class SidebandApp(MDApp): self.settings_screen.ids.display_style_in_contact_list.active = self.sideband.config["display_style_in_contact_list"] self.settings_screen.ids.display_style_in_contact_list.bind(active=save_display_style_in_contact_list) + self.settings_screen.ids.display_style_from_trusted_only.active = not self.sideband.config["display_style_from_all"] + self.settings_screen.ids.display_style_from_trusted_only.bind(active=save_display_style_from_trusted_only) + self.settings_screen.ids.settings_advanced_statistics.active = self.sideband.config["advanced_stats"] self.settings_screen.ids.settings_advanced_statistics.bind(active=save_advanced_stats) @@ -4629,22 +4664,22 @@ class SidebandApp(MDApp): def close_sub_map_action(self, sender=None): self.map_action(direction="right") - def object_details_action(self, sender=None, from_conv=False, from_telemetry=False, source_dest=None, direction="left"): + def object_details_action(self, sender=None, from_conv=False, from_objects=False, from_telemetry=False, source_dest=None, direction="left"): if self.root.ids.screen_manager.has_screen("object_details_screen"): - self.object_details_open(sender=sender, from_conv=from_conv, from_telemetry=from_telemetry, source_dest=source_dest, direction=direction) + self.object_details_open(sender=sender, from_conv=from_conv, from_objects=from_objects, from_telemetry=from_telemetry, source_dest=source_dest, direction=direction) else: self.loader_action(direction=direction) def final(dt): self.object_details_init() def o(dt): - self.object_details_open(sender=sender, from_conv=from_conv, from_telemetry=from_telemetry, source_dest=source_dest, no_transition=True) + self.object_details_open(sender=sender, from_conv=from_conv, from_objects=from_objects, from_telemetry=from_telemetry, source_dest=source_dest, no_transition=True) Clock.schedule_once(o, ll_ot) Clock.schedule_once(final, ll_ft) def object_details_init(self): self.object_details_screen = ObjectDetails(self) - def object_details_open(self, sender=None, from_conv=False, from_telemetry=False, source_dest=None, direction="left", no_transition=False): + def object_details_open(self, sender=None, from_conv=False, from_objects=False, from_telemetry=False, source_dest=None, direction="left", no_transition=False): if no_transition: self.root.ids.screen_manager.transition = self.no_transition else: @@ -4665,10 +4700,13 @@ class SidebandApp(MDApp): self.root.ids.nav_drawer.set_state("closed") if telemetry_source == None: - self.conversations_action(direction="right") + if self.include_objects and not self.include_conversations: + self.objects_action(direction="right") + else: + self.conversations_action(direction="right") else: - Clock.schedule_once(lambda dt: self.object_details_screen.set_source(telemetry_source, from_conv=from_conv, from_telemetry=from_telemetry), 0.0) + Clock.schedule_once(lambda dt: self.object_details_screen.set_source(telemetry_source, from_conv=from_conv, from_objects=from_objects, from_telemetry=from_telemetry), 0.0) def vj(dt): self.root.ids.screen_manager.current = "object_details_screen" diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index ba64c47..9c650e4 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -564,6 +564,8 @@ class SidebandCore(): self.config["telemetry_send_appearance"] = False if not "telemetry_display_trusted_only" in self.config: self.config["telemetry_display_trusted_only"] = False + if not "display_style_from_all" in self.config: + self.config["display_style_from_all"] = False if not "telemetry_receive_trusted_only" in self.config: self.config["telemetry_receive_trusted_only"] = False @@ -809,8 +811,8 @@ class SidebandCore(): except Exception as e: RNS.log("Exception while decoding LXMF destination announce data:"+str(e)) - def list_conversations(self): - result = self._db_conversations() + def list_conversations(self, conversations=True, objects=False): + result = self._db_conversations(conversations, objects) if result != None: return result else: @@ -849,10 +851,33 @@ class SidebandCore(): RNS.log("Error while checking trust for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR) return False - def should_send_telemetry(self, context_dest): + def is_object(self, context_dest, conv_data = None): + try: + if conv_data == None: + existing_conv = self._db_conversation(context_dest) + else: + existing_conv = conv_data + + if existing_conv != None: + data_dict = existing_conv["data"] + if data_dict != None: + if "is_object" in data_dict: + return data_dict["is_object"] + + return False + + except Exception as e: + RNS.log("Error while checking trust for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR) + return False + + def should_send_telemetry(self, context_dest, conv_data=None): try: if self.config["telemetry_enabled"]: - existing_conv = self._db_conversation(context_dest) + if conv_data == None: + existing_conv = self._db_conversation(context_dest) + else: + existing_conv = conv_data + if existing_conv != None: cd = existing_conv["data"] if cd != None and "telemetry" in cd and cd["telemetry"] == True: @@ -887,9 +912,13 @@ class SidebandCore(): RNS.log("Error while checking request permissions for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR) return False - def requests_allowed_from(self, context_dest): + def requests_allowed_from(self, context_dest, conv_data=None): try: - existing_conv = self._db_conversation(context_dest) + if conv_data == None: + existing_conv = self._db_conversation(context_dest) + else: + existing_conv = conv_data + if existing_conv != None: cd = existing_conv["data"] if cd != None and "allow_requests" in cd and cd["allow_requests"] == True: @@ -992,6 +1021,9 @@ class SidebandCore(): def untrusted_conversation(self, context_dest): self._db_conversation_set_trusted(context_dest, False) + def conversation_set_object(self, context_dest, is_object): + self._db_conversation_set_object(context_dest, is_object) + def send_telemetry_in_conversation(self, context_dest): self._db_conversation_set_telemetry(context_dest, True) @@ -1954,6 +1986,24 @@ class SidebandCore(): result = dbc.fetchall() db.commit() + def _db_conversation_set_object(self, context_dest, is_object=False): + conv = self._db_conversation(context_dest) + data_dict = conv["data"] + if data_dict == None: + data_dict = {} + + data_dict["is_object"] = is_object + packed_dict = msgpack.packb(data_dict) + + db = self.__db_connect() + dbc = db.cursor() + + query = "UPDATE conv set data = ? where dest_context = ?" + data = (packed_dict, context_dest) + dbc.execute(query, data) + result = dbc.fetchall() + db.commit() + def _db_conversation_set_trusted(self, context_dest, trusted): db = self.__db_connect() dbc = db.cursor() @@ -1973,7 +2023,7 @@ class SidebandCore(): result = dbc.fetchall() db.commit() - def _db_conversations(self): + def _db_conversations(self, conversations=True, objects=False): db = self.__db_connect() dbc = db.cursor() @@ -1985,12 +2035,15 @@ class SidebandCore(): else: convs = [] for entry in result: + is_object = False last_rx = entry[1] last_tx = entry[2] last_activity = max(last_rx, last_tx) data = None try: data = msgpack.unpackb(entry[7]) + if "is_object" in data: + is_object = data["is_object"] except: pass @@ -2003,7 +2056,14 @@ class SidebandCore(): "trust": entry[5], "data": data, } - convs.append(conv) + should_add = False + if conversations and not is_object: + should_add = True + if objects and is_object: + should_add = True + + if should_add: + convs.append(conv) return sorted(convs, key=lambda c: c["last_activity"], reverse=True) diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py index 6f7f207..57eae2e 100644 --- a/sbapp/ui/conversations.py +++ b/sbapp/ui/conversations.py @@ -35,6 +35,7 @@ class ConvSettings(BoxLayout): trusted = BooleanProperty() telemetry = BooleanProperty() allow_requests = BooleanProperty() + is_object = BooleanProperty() class Conversations(): def __init__(self, app): @@ -72,7 +73,16 @@ class Conversations(): # if self.app.sideband.getstate("app.flags.unread_conversations"): # self.clear_list() - self.context_dests = self.app.sideband.list_conversations() + self.context_dests = self.app.sideband.list_conversations(conversations=self.app.include_conversations, objects=self.app.include_objects) + + view_title = "Conversations" + if self.app.include_conversations: + if self.app.include_objects: + view_title = "Conversations & Objects" + elif self.app.include_objects: + view_title = "Objects" + self.screen.ids.conversations_bar.title = view_title + self.update_widget() self.app.sideband.setstate("app.flags.unread_conversations", False) @@ -83,12 +93,12 @@ class Conversations(): context_dest = conv["dest"] unread = conv["unread"] appearance = self.app.sideband.peer_appearance(context_dest, conv=conv) - # is_trusted = self.app.sideband.is_trusted(context_dest) is_trusted = conv["trust"] == 1 + appearance_from_all = self.app.sideband.config["display_style_from_all"] trust_icon = "account-question" da = self.app.sideband.DEFAULT_APPEARANCE - if is_trusted and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da: + if (is_trusted or appearance_from_all) and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da: if unread: trust_icon = "email" else: @@ -123,6 +133,7 @@ class Conversations(): last_activity = conv["last_activity"] trusted = conv["trust"] == 1 appearance = self.app.sideband.peer_appearance(context_dest, conv=conv) + is_object = self.app.sideband.is_object(context_dest, conv_data=conv) da = self.app.sideband.DEFAULT_APPEARANCE ic_s = 24; ic_p = 14 @@ -135,10 +146,21 @@ class Conversations(): else: ti_color = None + if is_object: + def gen_rel_func(): + def x(ws): + self.app.object_details_action(sender=ws, from_objects=True) + return x + + rel_func = gen_rel_func() + else: + rel_func = self.app.conversation_action + iconl = IconLeftWidget( icon=conv_icon, theme_icon_color=ti_color, icon_color=fg, md_bg_color=bg, - on_release=self.app.conversation_action) + on_release=rel_func) + iconl.source_dest = context_dest iconl._default_icon_pad = dp(ic_p) iconl.icon_size = dp(ic_s) @@ -154,7 +176,7 @@ class Conversations(): remove_widgets = [] for w in self.list.children: if not w.sb_uid in [e["dest"] for e in self.context_dests]: - RNS.log("Should remove "+RNS.prettyhexrep(w.sb_uid)+" from list") + RNS.log("Should remove "+RNS.prettyhexrep(w.sb_uid)+" from list", RNS.LOG_DEBUG) remove_widgets.append(w) self.added_item_dests.remove(w.sb_uid) @@ -169,7 +191,9 @@ class Conversations(): last_activity = conv["last_activity"] peer_disp_name = multilingual_markup(escape_markup(str(self.app.sideband.peer_display_name(context_dest))).encode("utf-8")).decode("utf-8") - if not context_dest in self.added_item_dests: + if not context_dest in self.added_item_dests: + existing_conv = self.app.sideband._db_conversation(context_dest) + is_object = self.app.sideband.is_object(context_dest, conv_data=existing_conv) iconl = self.get_icon(conv) item = OneLineAvatarIconListItem(text=peer_disp_name, on_release=self.app.conversation_action) item.add_widget(iconl) @@ -184,14 +208,18 @@ class Conversations(): t_s = time.time() dest = self.conversation_dropdown.context_dest try: + cd = self.app.sideband._db_conversation(dest) disp_name = self.app.sideband.raw_display_name(dest) - is_trusted = self.app.sideband.is_trusted(dest) - send_telemetry = self.app.sideband.should_send_telemetry(dest) - allow_requests = self.app.sideband.requests_allowed_from(dest) + is_trusted = self.app.sideband.is_trusted(dest, conv_data=cd) + is_object = self.app.sideband.is_object(dest, conv_data=cd) + send_telemetry = self.app.sideband.should_send_telemetry(dest, conv_data=cd) + allow_requests = self.app.sideband.requests_allowed_from(dest, conv_data=cd) + RNS.log("is_object: "+str(is_object)) yes_button = MDRectangleFlatButton(text="Save",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_accept, text_color=self.app.color_accept) no_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18)) - dialog_content = ConvSettings(disp_name=disp_name, context_dest=RNS.hexrep(dest, delimit=False), trusted=is_trusted, telemetry=send_telemetry, allow_requests=allow_requests) + dialog_content = ConvSettings(disp_name=disp_name, context_dest=RNS.hexrep(dest, delimit=False), trusted=is_trusted, + telemetry=send_telemetry, allow_requests=allow_requests, is_object=is_object) if self.app.sideband.config["input_language"] != None: dialog_content.ids.name_field.font_name = self.app.sideband.config["input_language"] else: @@ -212,6 +240,7 @@ class Conversations(): trusted = dialog.d_content.ids["trusted_switch"].active telemetry = dialog.d_content.ids["telemetry_switch"].active allow_requests = dialog.d_content.ids["allow_requests_switch"].active + conv_is_object = dialog.d_content.ids["is_object_switch"].active if trusted: self.app.sideband.trusted_conversation(dest) else: @@ -227,6 +256,11 @@ class Conversations(): else: self.app.sideband.disallow_requests_from(dest) + if conv_is_object: + self.app.sideband.conversation_set_object(dest, True) + else: + self.app.sideband.conversation_set_object(dest, False) + self.app.sideband.named_conversation(name, dest) except Exception as e: @@ -326,6 +360,13 @@ class Conversations(): self.delete_dialog.open() return x + # def gen_move_to(item): + # def x(): + # item.dmenu.dismiss() + # self.app.sideband.conversation_set_object(self.conversation_dropdown.context_dest, not self.app.sideband.is_object(self.conversation_dropdown.context_dest)) + # self.app.conversations_view.update() + # return x + def gen_copy_addr(item): def x(): Clipboard.copy(RNS.hexrep(self.conversation_dropdown.context_dest, delimit=False)) @@ -335,6 +376,7 @@ class Conversations(): item.iconr = IconRightWidget(icon="dots-vertical"); if self.conversation_dropdown == None: + obj_str = "conversations" if is_object else "objects" dmi_h = 40 dm_items = [ { @@ -349,6 +391,12 @@ class Conversations(): "height": dp(dmi_h), "on_release": gen_copy_addr(item) }, + # { + # "text": "Move to objects", + # "viewclass": "OneLineListItem", + # "height": dp(dmi_h), + # "on_release": gen_move_to(item) + # }, { "text": "Clear Messages", "viewclass": "OneLineListItem", @@ -393,7 +441,7 @@ class Conversations(): item.add_widget(item.iconr) - item.trusted = self.app.sideband.is_trusted(context_dest) + item.trusted = self.app.sideband.is_trusted(context_dest, conv_data=existing_conv) self.added_item_dests.append(context_dest) self.list.add_widget(item) @@ -440,6 +488,7 @@ MDScreen: MDTopAppBar: title: "Conversations" + id: conversations_bar anchor_title: "left" elevation: 0 left_action_items: @@ -561,6 +610,21 @@ Builder.load_string(""" pos_hint: {"center_y": 0.43} active: root.allow_requests + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(8),0] + height: dp(32) + MDLabel: + id: is_object_label + text: "Is Object" + font_style: "H6" + + MDSwitch: + id: is_object_switch + pos_hint: {"center_y": 0.43} + active: root.is_object + orientation: "vertical" spacing: "24dp" diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index 69db515..f9752ab 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -61,6 +61,16 @@ MDNavigationLayout: on_release: root.ids.screen_manager.app.conversations_action(self) + OneLineIconListItem: + text: "Objects" + on_release: root.ids.screen_manager.app.objects_action(self) + # _no_ripple_effect: True + + IconLeftWidget: + icon: "devices" + on_release: root.ids.screen_manager.app.objects_action(self) + + OneLineIconListItem: text: "Situation Map" on_release: root.ids.screen_manager.app.map_action(self) @@ -1402,7 +1412,7 @@ MDScreen: height: dp(48) MDLabel: - text: "Display styles in conversation list" + text: "Show user icons in conversation list" font_style: "H6" MDSwitch: @@ -1410,6 +1420,21 @@ MDScreen: pos_hint: {"center_y": 0.3} active: False + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(24),dp(0)] + height: dp(48) + + MDLabel: + text: "Only show user icons from trusted" + font_style: "H6" + + MDSwitch: + id: display_style_from_trusted_only + pos_hint: {"center_y": 0.3} + active: False + MDBoxLayout: orientation: "horizontal" size_hint_y: None diff --git a/sbapp/ui/objectdetails.py b/sbapp/ui/objectdetails.py index 43d9adf..2950239 100644 --- a/sbapp/ui/objectdetails.py +++ b/sbapp/ui/objectdetails.py @@ -41,6 +41,7 @@ class ObjectDetails(): self.raw_telemetry = None self.from_telemetry = False self.from_conv = False + self.from_objects = False self.viewing_self = False self.delete_dialog = None @@ -84,6 +85,8 @@ class ObjectDetails(): else: if self.from_conv: self.app.open_conversation(self.object_hash, direction="right") + elif self.from_objects: + self.app.objects_action(direction="right") else: self.app.close_sub_map_action() @@ -117,11 +120,11 @@ class ObjectDetails(): def reload_telemetry(self, sender=None, notoast=False): if self.object_hash != None: - self.set_source(self.object_hash, from_conv=self.from_conv, from_telemetry=self.from_telemetry) + self.set_source(self.object_hash, from_conv=self.from_conv, from_objects=self.from_objects, from_telemetry=self.from_telemetry) if not notoast: toast("Reloaded telemetry for object") - def set_source(self, source_dest, from_conv=False, from_telemetry=False, prefetched=None): + def set_source(self, source_dest, from_conv=False, from_objects=False, from_telemetry=False, prefetched=None): try: self.object_hash = source_dest own_address = self.app.sideband.lxmf_destination.hash @@ -140,6 +143,10 @@ class ObjectDetails(): self.from_conv = True else: self.from_conv = False + if from_objects: + self.from_objects = True + else: + self.from_objects = False self.coords = None self.telemetry_list.data = []