Mercurial > libervia-backend
comparison libervia/frontends/quick_frontend/quick_app.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | d01b8d002619 |
children |
comparison
equal
deleted
inserted
replaced
4269:64a85ce8be70 | 4270:0d7bb4df2343 |
---|---|
42 | 42 |
43 # TODO: handle waiting XMLUI requests: getWaitingConf doesn't exist anymore | 43 # TODO: handle waiting XMLUI requests: getWaitingConf doesn't exist anymore |
44 # and a way to keep some XMLUI request between sessions is expected in backend | 44 # and a way to keep some XMLUI request between sessions is expected in backend |
45 host = None | 45 host = None |
46 bridge = None | 46 bridge = None |
47 cache_keys_to_get = ['avatar', 'nicknames'] | 47 cache_keys_to_get = ["avatar", "nicknames"] |
48 | 48 |
49 def __init__(self, profile): | 49 def __init__(self, profile): |
50 self.profile = profile | 50 self.profile = profile |
51 self.connected = False | 51 self.connected = False |
52 self.whoami = None | 52 self.whoami = None |
134 log.error("Couldn't get features: {}".format(failure)) | 134 log.error("Couldn't get features: {}".format(failure)) |
135 self._plug_profile_get_features_cb({}) | 135 self._plug_profile_get_features_cb({}) |
136 | 136 |
137 def _plug_profile_get_features_cb(self, features): | 137 def _plug_profile_get_features_cb(self, features): |
138 self.host.features = features | 138 self.host.features = features |
139 self.host.bridge.entities_data_get([], ProfileManager.cache_keys_to_get, | 139 self.host.bridge.entities_data_get( |
140 profile=self.profile, | 140 [], |
141 callback=self._plug_profile_got_cached_values, | 141 ProfileManager.cache_keys_to_get, |
142 errback=self._plug_profile_failed_cached_values) | 142 profile=self.profile, |
143 callback=self._plug_profile_got_cached_values, | |
144 errback=self._plug_profile_failed_cached_values, | |
145 ) | |
143 | 146 |
144 def _plug_profile_failed_cached_values(self, failure): | 147 def _plug_profile_failed_cached_values(self, failure): |
145 log.error("Couldn't get cached values: {}".format(failure)) | 148 log.error("Couldn't get cached values: {}".format(failure)) |
146 self._plug_profile_got_cached_values({}) | 149 self._plug_profile_got_cached_values({}) |
147 | 150 |
151 for entity_s, data in cached_values.items(): | 154 for entity_s, data in cached_values.items(): |
152 for key, value in data.items(): | 155 for key, value in data.items(): |
153 self.host.entity_data_updated_handler(entity_s, key, value, self.profile) | 156 self.host.entity_data_updated_handler(entity_s, key, value, self.profile) |
154 | 157 |
155 if not self.connected: | 158 if not self.connected: |
156 self.host.set_presence_status(C.PRESENCE_UNAVAILABLE, "", profile=self.profile) | 159 self.host.set_presence_status( |
160 C.PRESENCE_UNAVAILABLE, "", profile=self.profile | |
161 ) | |
157 else: | 162 else: |
158 | 163 |
159 contact_list.fill() | 164 contact_list.fill() |
160 self.host.set_presence_status(profile=self.profile) | 165 self.host.set_presence_status(profile=self.profile) |
161 | 166 |
256 #: if False, frontend must call resync itself when suitable (e.g. widget is being | 261 #: if False, frontend must call resync itself when suitable (e.g. widget is being |
257 #: visible) | 262 #: visible) |
258 AUTO_RESYNC = True | 263 AUTO_RESYNC = True |
259 | 264 |
260 def __init__( | 265 def __init__( |
261 self, bridge_factory, xmlui, check_options=None, connect_bridge=True, | 266 self, |
262 async_bridge_factory=None | 267 bridge_factory, |
268 xmlui, | |
269 check_options=None, | |
270 connect_bridge=True, | |
271 async_bridge_factory=None, | |
263 ): | 272 ): |
264 """Create a frontend application | 273 """Create a frontend application |
265 | 274 |
266 @param bridge_factory: method to use to create the bridge | 275 @param bridge_factory: method to use to create the bridge |
267 @param xmlui: xmlui module | 276 @param xmlui: xmlui module |
274 self.profiles = ProfilesManager() | 283 self.profiles = ProfilesManager() |
275 # profiles currently being plugged, used to (un)lock contact list updates | 284 # profiles currently being plugged, used to (un)lock contact list updates |
276 self._plugs_in_progress = set() | 285 self._plugs_in_progress = set() |
277 self.ready_profiles = set() # profiles which are connected and ready | 286 self.ready_profiles = set() # profiles which are connected and ready |
278 self.signals_cache = {} # used to keep signal received between start of | 287 self.signals_cache = {} # used to keep signal received between start of |
279 # plug_profile and when the profile is actualy ready | 288 # plug_profile and when the profile is actualy ready |
280 self.contact_lists = quick_contact_list.QuickContactListHandler(self) | 289 self.contact_lists = quick_contact_list.QuickContactListHandler(self) |
281 self.widgets = quick_widgets.QuickWidgetsManager(self) | 290 self.widgets = quick_widgets.QuickWidgetsManager(self) |
282 if check_options is not None: | 291 if check_options is not None: |
283 self.options = check_options() | 292 self.options = check_options() |
284 else: | 293 else: |
287 # see selected_widget setter and getter | 296 # see selected_widget setter and getter |
288 self._selected_widget = None | 297 self._selected_widget = None |
289 | 298 |
290 # listeners are callable watching events | 299 # listeners are callable watching events |
291 self._listeners = {} # key: listener type ("avatar", "selected", etc), | 300 self._listeners = {} # key: listener type ("avatar", "selected", etc), |
292 # value: list of callbacks | 301 # value: list of callbacks |
293 | 302 |
294 # cf. [register_action_handler] | 303 # cf. [register_action_handler] |
295 self._action_handlers: dict[str, Callable[[dict, str, int, str], None]] = {} | 304 self._action_handlers: dict[str, Callable[[dict, str, int, str], None]] = {} |
296 | 305 |
297 # triggers | 306 # triggers |
389 print((_("Error while initialising bridge: {}".format(failure)))) | 398 print((_("Error while initialising bridge: {}".format(failure)))) |
390 | 399 |
391 def on_backend_ready(self): | 400 def on_backend_ready(self): |
392 log.info("backend is ready") | 401 log.info("backend is ready") |
393 self.bridge.namespaces_get( | 402 self.bridge.namespaces_get( |
394 callback=self._namespaces_get_cb, errback=self._namespaces_get_eb) | 403 callback=self._namespaces_get_cb, errback=self._namespaces_get_eb |
404 ) | |
395 # we cache available encryption plugins, as we'll use them on each | 405 # we cache available encryption plugins, as we'll use them on each |
396 # new chat widget | 406 # new chat widget |
397 self.bridge.encryption_plugins_get( | 407 self.bridge.encryption_plugins_get( |
398 callback=self._encryption_plugins_get_cb, | 408 callback=self._encryption_plugins_get_cb, |
399 errback=self._encryption_plugins_get_eb) | 409 errback=self._encryption_plugins_get_eb, |
400 | 410 ) |
401 | 411 |
402 @property | 412 @property |
403 def current_profile(self): | 413 def current_profile(self): |
404 """Profile that a user would expect to use""" | 414 """Profile that a user would expect to use""" |
405 try: | 415 try: |
594 self._listeners[type_].pop(callback) | 604 self._listeners[type_].pop(callback) |
595 except KeyError: | 605 except KeyError: |
596 if not ignore_missing: | 606 if not ignore_missing: |
597 log.error( | 607 log.error( |
598 f"Trying to remove an inexisting listener (type = {type_}): " | 608 f"Trying to remove an inexisting listener (type = {type_}): " |
599 f"{callback}") | 609 f"{callback}" |
610 ) | |
600 | 611 |
601 def call_listeners(self, type_, *args, **kwargs): | 612 def call_listeners(self, type_, *args, **kwargs): |
602 """Call the methods which listen type_ event. If a profiles filter has | 613 """Call the methods which listen type_ event. If a profiles filter has |
603 been register with a listener and profile argument is not None, the | 614 been register with a listener and profile argument is not None, the |
604 listener will be called only if profile is in the profiles filter list. | 615 listener will be called only if profile is in the profiles filter list. |
767 ) | 778 ) |
768 | 779 |
769 def contact_new_handler(self, jid_s, attributes, groups, profile): | 780 def contact_new_handler(self, jid_s, attributes, groups, profile): |
770 entity = jid.JID(jid_s) | 781 entity = jid.JID(jid_s) |
771 groups = list(groups) | 782 groups = list(groups) |
772 self.contact_lists[profile].set_contact(entity, groups, attributes, in_roster=True) | 783 self.contact_lists[profile].set_contact( |
784 entity, groups, attributes, in_roster=True | |
785 ) | |
773 | 786 |
774 def message_new_handler( | 787 def message_new_handler( |
775 self, uid, timestamp, from_jid_s, to_jid_s, msg, subject, type_, extra_s, | 788 self, uid, timestamp, from_jid_s, to_jid_s, msg, subject, type_, extra_s, profile |
776 profile): | 789 ): |
777 from_jid = jid.JID(from_jid_s) | 790 from_jid = jid.JID(from_jid_s) |
778 to_jid = jid.JID(to_jid_s) | 791 to_jid = jid.JID(to_jid_s) |
779 extra = data_format.deserialise(extra_s) | 792 extra = data_format.deserialise(extra_s) |
780 if not self.trigger.point( | 793 if not self.trigger.point( |
781 "messageNewTrigger", uid, timestamp, from_jid, to_jid, msg, subject, type_, | 794 "messageNewTrigger", |
782 extra, profile=profile,): | 795 uid, |
796 timestamp, | |
797 from_jid, | |
798 to_jid, | |
799 msg, | |
800 subject, | |
801 type_, | |
802 extra, | |
803 profile=profile, | |
804 ): | |
783 return | 805 return |
784 | 806 |
785 from_me = from_jid.bare == self.profiles[profile].whoami.bare | 807 from_me = from_jid.bare == self.profiles[profile].whoami.bare |
786 mess_to_jid = to_jid if from_me else from_jid | 808 mess_to_jid = to_jid if from_me else from_jid |
787 target = mess_to_jid.bare | 809 target = mess_to_jid.bare |
798 target = target | 820 target = target |
799 # we want to be sure to have at least one QuickChat instance | 821 # we want to be sure to have at least one QuickChat instance |
800 self.widgets.get_or_create_widget( | 822 self.widgets.get_or_create_widget( |
801 quick_chat.QuickChat, | 823 quick_chat.QuickChat, |
802 target, | 824 target, |
803 type_ = C.CHAT_GROUP if is_room else C.CHAT_ONE2ONE, | 825 type_=C.CHAT_GROUP if is_room else C.CHAT_ONE2ONE, |
804 on_new_widget = None, | 826 on_new_widget=None, |
805 profile = profile, | 827 profile=profile, |
806 ) | 828 ) |
807 | 829 |
808 if ( | 830 if ( |
809 not from_jid in contact_list | 831 not from_jid in contact_list |
810 and from_jid.bare != self.profiles[profile].whoami.bare | 832 and from_jid.bare != self.profiles[profile].whoami.bare |
822 ) | 844 ) |
823 | 845 |
824 def message_encryption_started_handler(self, destinee_jid_s, plugin_data, profile): | 846 def message_encryption_started_handler(self, destinee_jid_s, plugin_data, profile): |
825 destinee_jid = jid.JID(destinee_jid_s) | 847 destinee_jid = jid.JID(destinee_jid_s) |
826 plugin_data = data_format.deserialise(plugin_data) | 848 plugin_data = data_format.deserialise(plugin_data) |
827 for widget in self.widgets.get_widgets(quick_chat.QuickChat, | 849 for widget in self.widgets.get_widgets( |
828 target=destinee_jid.bare, | 850 quick_chat.QuickChat, target=destinee_jid.bare, profiles=(profile,) |
829 profiles=(profile,)): | 851 ): |
830 widget.message_encryption_started(plugin_data) | 852 widget.message_encryption_started(plugin_data) |
831 | 853 |
832 def message_encryption_stopped_handler(self, destinee_jid_s, plugin_data, profile): | 854 def message_encryption_stopped_handler(self, destinee_jid_s, plugin_data, profile): |
833 destinee_jid = jid.JID(destinee_jid_s) | 855 destinee_jid = jid.JID(destinee_jid_s) |
834 for widget in self.widgets.get_widgets(quick_chat.QuickChat, | 856 for widget in self.widgets.get_widgets( |
835 target=destinee_jid.bare, | 857 quick_chat.QuickChat, target=destinee_jid.bare, profiles=(profile,) |
836 profiles=(profile,)): | 858 ): |
837 widget.message_encryption_stopped(plugin_data) | 859 widget.message_encryption_stopped(plugin_data) |
838 | 860 |
839 def message_state_handler(self, uid, status, profile): | 861 def message_state_handler(self, uid, status, profile): |
840 for widget in self.widgets.get_widgets(quick_chat.QuickChat, profiles=(profile,)): | 862 for widget in self.widgets.get_widgets(quick_chat.QuickChat, profiles=(profile,)): |
841 widget.on_message_state(uid, status, profile) | 863 widget.on_message_state(uid, status, profile) |
842 | 864 |
843 def message_send(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): | 865 def message_send( |
844 if not subject and not extra and (not message or message == {'': ''}): | 866 self, |
867 to_jid, | |
868 message, | |
869 subject=None, | |
870 mess_type="auto", | |
871 extra=None, | |
872 callback=None, | |
873 errback=None, | |
874 profile_key=C.PROF_KEY_NONE, | |
875 ): | |
876 if not subject and not extra and (not message or message == {"": ""}): | |
845 log.debug("Not sending empty message") | 877 log.debug("Not sending empty message") |
846 return | 878 return |
847 | 879 |
848 if subject is None: | 880 if subject is None: |
849 subject = {} | 881 subject = {} |
851 extra = {} | 883 extra = {} |
852 if callback is None: | 884 if callback is None: |
853 callback = ( | 885 callback = ( |
854 lambda __=None: None | 886 lambda __=None: None |
855 ) # FIXME: optional argument is here because pyjamas doesn't support callback | 887 ) # FIXME: optional argument is here because pyjamas doesn't support callback |
856 # without arg with json proxy | 888 # without arg with json proxy |
857 if errback is None: | 889 if errback is None: |
858 errback = lambda failure: self.show_dialog( | 890 errback = lambda failure: self.show_dialog( |
859 message=failure.message, title=failure.fullname, type="error" | 891 message=failure.message, title=failure.fullname, type="error" |
860 ) | 892 ) |
861 | 893 |
862 if not self.trigger.point("messageSendTrigger", to_jid, message, subject, mess_type, extra, callback, errback, profile_key=profile_key): | 894 if not self.trigger.point( |
895 "messageSendTrigger", | |
896 to_jid, | |
897 message, | |
898 subject, | |
899 mess_type, | |
900 extra, | |
901 callback, | |
902 errback, | |
903 profile_key=profile_key, | |
904 ): | |
863 return | 905 return |
864 | 906 |
865 self.bridge.message_send( | 907 self.bridge.message_send( |
866 str(to_jid), | 908 str(to_jid), |
867 message, | 909 message, |
904 return | 946 return |
905 | 947 |
906 self.call_listeners("presence", entity, show, priority, statuses, profile=profile) | 948 self.call_listeners("presence", entity, show, priority, statuses, profile=profile) |
907 | 949 |
908 def muc_room_joined_handler( | 950 def muc_room_joined_handler( |
909 self, room_jid_s, occupants, user_nick, subject, statuses, profile): | 951 self, room_jid_s, occupants, user_nick, subject, statuses, profile |
952 ): | |
910 """Called when a MUC room is joined""" | 953 """Called when a MUC room is joined""" |
911 log.debug( | 954 log.debug( |
912 "Room [{room_jid}] joined by {profile}, users presents:{users}".format( | 955 "Room [{room_jid}] joined by {profile}, users presents:{users}".format( |
913 room_jid=room_jid_s, profile=profile, users=list(occupants.keys()) | 956 room_jid=room_jid_s, profile=profile, users=list(occupants.keys()) |
914 ) | 957 ) |
934 ) | 977 ) |
935 room_jid = jid.JID(room_jid_s) | 978 room_jid = jid.JID(room_jid_s) |
936 chat_widget = self.widgets.get_widget(quick_chat.QuickChat, room_jid, profile) | 979 chat_widget = self.widgets.get_widget(quick_chat.QuickChat, room_jid, profile) |
937 if chat_widget: | 980 if chat_widget: |
938 self.widgets.delete_widget( | 981 self.widgets.delete_widget( |
939 chat_widget, all_instances=True, explicit_close=True) | 982 chat_widget, all_instances=True, explicit_close=True |
983 ) | |
940 self.contact_lists[profile].remove_contact(room_jid) | 984 self.contact_lists[profile].remove_contact(room_jid) |
941 | 985 |
942 def muc_room_user_changed_nick_handler(self, room_jid_s, old_nick, new_nick, profile): | 986 def muc_room_user_changed_nick_handler(self, room_jid_s, old_nick, new_nick, profile): |
943 """Called when an user joined a MUC room""" | 987 """Called when an user joined a MUC room""" |
944 room_jid = jid.JID(room_jid_s) | 988 room_jid = jid.JID(room_jid_s) |
969 @param from_jid_s (unicode): JID of a contact or C.ENTITY_ALL | 1013 @param from_jid_s (unicode): JID of a contact or C.ENTITY_ALL |
970 @param state (unicode): new state | 1014 @param state (unicode): new state |
971 @param profile (unicode): current profile | 1015 @param profile (unicode): current profile |
972 """ | 1016 """ |
973 from_jid = jid.JID(from_jid_s) | 1017 from_jid = jid.JID(from_jid_s) |
974 for widget in self.widgets.get_widgets(quick_chat.QuickChat, target=from_jid.bare, | 1018 for widget in self.widgets.get_widgets( |
975 profiles=(profile,)): | 1019 quick_chat.QuickChat, target=from_jid.bare, profiles=(profile,) |
1020 ): | |
976 widget.on_chat_state(from_jid, state, profile) | 1021 widget.on_chat_state(from_jid, state, profile) |
977 | 1022 |
978 def notify(self, type_, entity=None, message=None, subject=None, callback=None, | 1023 def notify( |
979 cb_args=None, widget=None, profile=C.PROF_KEY_NONE): | 1024 self, |
1025 type_, | |
1026 entity=None, | |
1027 message=None, | |
1028 subject=None, | |
1029 callback=None, | |
1030 cb_args=None, | |
1031 widget=None, | |
1032 profile=C.PROF_KEY_NONE, | |
1033 ): | |
980 """Trigger an event notification | 1034 """Trigger an event notification |
981 | 1035 |
982 @param type_(unicode): notifation kind, | 1036 @param type_(unicode): notifation kind, |
983 one of C.NOTIFY_* constant or any custom type specific to frontend | 1037 one of C.NOTIFY_* constant or any custom type specific to frontend |
984 @param entity(jid.JID, None): entity involved in the notification | 1038 @param entity(jid.JID, None): entity involved in the notification |
1007 type_notifs.append(notif_data) | 1061 type_notifs.append(notif_data) |
1008 self._notifications[self._notif_id] = notif_data | 1062 self._notifications[self._notif_id] = notif_data |
1009 self._notif_id += 1 | 1063 self._notif_id += 1 |
1010 self.call_listeners("notification", entity, notif_data, profile=profile) | 1064 self.call_listeners("notification", entity, notif_data, profile=profile) |
1011 | 1065 |
1012 def get_notifs(self, entity=None, type_=None, exact_jid=None, profile=C.PROF_KEY_NONE): | 1066 def get_notifs( |
1067 self, entity=None, type_=None, exact_jid=None, profile=C.PROF_KEY_NONE | |
1068 ): | |
1013 """return notifications for given entity | 1069 """return notifications for given entity |
1014 | 1070 |
1015 @param entity(jid.JID, None, C.ENTITY_ALL): jid of the entity to check | 1071 @param entity(jid.JID, None, C.ENTITY_ALL): jid of the entity to check |
1016 bare jid to get all notifications, full jid to filter on resource | 1072 bare jid to get all notifications, full jid to filter on resource |
1017 None to get general notifications | 1073 None to get general notifications |
1201 ) | 1257 ) |
1202 | 1258 |
1203 def _debug_handler(self, action, parameters, profile): | 1259 def _debug_handler(self, action, parameters, profile): |
1204 if action == "widgets_dump": | 1260 if action == "widgets_dump": |
1205 from pprint import pformat | 1261 from pprint import pformat |
1262 | |
1206 log.info("Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets))) | 1263 log.info("Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets))) |
1207 else: | 1264 else: |
1208 log.warning("Unknown debug action: {action}".format(action=action)) | 1265 log.warning("Unknown debug action: {action}".format(action=action)) |
1209 | |
1210 | 1266 |
1211 def show_dialog(self, message, title, type="info", answer_cb=None, answer_data=None): | 1267 def show_dialog(self, message, title, type="info", answer_cb=None, answer_data=None): |
1212 """Show a dialog to user | 1268 """Show a dialog to user |
1213 | 1269 |
1214 Frontends must override this method | 1270 Frontends must override this method |
1274 assert isinstance(value, dict) or value is None | 1330 assert isinstance(value, dict) or value is None |
1275 self.contact_lists[profile].set_cache(entity, "avatar", value) | 1331 self.contact_lists[profile].set_cache(entity, "avatar", value) |
1276 self.call_listeners("avatar", entity, value, profile=profile) | 1332 self.call_listeners("avatar", entity, value, profile=profile) |
1277 | 1333 |
1278 def register_action_handler( | 1334 def register_action_handler( |
1279 self, | 1335 self, action_type: str, handler: Callable[[dict, str, int, str], None] |
1280 action_type: str, | |
1281 handler: Callable[[dict, str, int, str], None] | |
1282 ) -> None: | 1336 ) -> None: |
1283 """Register a handler for action type. | 1337 """Register a handler for action type. |
1284 | 1338 |
1285 If an action of this type is received, the handler will be used, otherwise, | 1339 If an action of this type is received, the handler will be used, otherwise, |
1286 generic ``action_manager`` is used | 1340 generic ``action_manager`` is used |
1305 self._action_handlers[action_type] = handler | 1359 self._action_handlers[action_type] = handler |
1306 | 1360 |
1307 def action_manager( | 1361 def action_manager( |
1308 self, | 1362 self, |
1309 action_data: dict, | 1363 action_data: dict, |
1310 callback: Callable|None = None, | 1364 callback: Callable | None = None, |
1311 ui_show_cb: Callable|None = None, | 1365 ui_show_cb: Callable | None = None, |
1312 user_action: bool = True, | 1366 user_action: bool = True, |
1313 action_id: str|None = None, | 1367 action_id: str | None = None, |
1314 progress_cb: Callable|None = None, | 1368 progress_cb: Callable | None = None, |
1315 progress_eb: Callable|None = None, | 1369 progress_eb: Callable | None = None, |
1316 profile: str = C.PROF_KEY_NONE | 1370 profile: str = C.PROF_KEY_NONE, |
1317 ) -> None: | 1371 ) -> None: |
1318 """Handle backend action | 1372 """Handle backend action |
1319 | 1373 |
1320 @param action_data: action dict as sent by action_launch or returned by an | 1374 @param action_data: action dict as sent by action_launch or returned by an |
1321 UI action | 1375 UI action |
1384 data = dict() | 1438 data = dict() |
1385 action_cb = lambda data: self._action_cb( | 1439 action_cb = lambda data: self._action_cb( |
1386 data_format.deserialise(data), callback, callback_id, profile | 1440 data_format.deserialise(data), callback, callback_id, profile |
1387 ) | 1441 ) |
1388 self.bridge.action_launch( | 1442 self.bridge.action_launch( |
1389 callback_id, data_format.serialise(data), profile, callback=action_cb, | 1443 callback_id, |
1390 errback=self.dialog_failure | 1444 data_format.serialise(data), |
1445 profile, | |
1446 callback=action_cb, | |
1447 errback=self.dialog_failure, | |
1391 ) | 1448 ) |
1392 | 1449 |
1393 def launch_menu( | 1450 def launch_menu( |
1394 self, | 1451 self, |
1395 menu_type, | 1452 menu_type, |