Mercurial > libervia-desktop-kivy
comparison cagou/plugins/plugin_wid_chat.py @ 491:203755bbe0fe
massive refactoring from camelCase -> snake_case. See backend commit log for more details
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:44:32 +0200 |
parents | 962d17c4078c |
children |
comparison
equal
deleted
inserted
replaced
490:962d17c4078c | 491:203755bbe0fe |
---|---|
98 attachments = properties.ObjectProperty() | 98 attachments = properties.ObjectProperty() |
99 reduce_checkbox = properties.ObjectProperty() | 99 reduce_checkbox = properties.ObjectProperty() |
100 show_resize = properties.BooleanProperty(False) | 100 show_resize = properties.BooleanProperty(False) |
101 | 101 |
102 def on_kv_post(self, __): | 102 def on_kv_post(self, __): |
103 self.attachments.bind(children=self.onAttachment) | 103 self.attachments.bind(children=self.on_attachment) |
104 | 104 |
105 def onAttachment(self, __, attachments): | 105 def on_attachment(self, __, attachments): |
106 if len(attachments) == 0: | 106 if len(attachments) == 0: |
107 self.show_resize = False | 107 self.show_resize = False |
108 | 108 |
109 | 109 |
110 class BaseAttachmentItem(BoxLayout): | 110 class BaseAttachmentItem(BoxLayout): |
138 image = properties.ObjectProperty() | 138 image = properties.ObjectProperty() |
139 | 139 |
140 def on_press(self): | 140 def on_press(self): |
141 full_size_source = self.data.get('path', self.data.get('url')) | 141 full_size_source = self.data.get('path', self.data.get('url')) |
142 gallery = ImagesGallery(sources=[full_size_source]) | 142 gallery = ImagesGallery(sources=[full_size_source]) |
143 G.host.showExtraUI(gallery) | 143 G.host.show_extra_ui(gallery) |
144 | 144 |
145 def on_kv_post(self, __): | 145 def on_kv_post(self, __): |
146 self.on_data(None, self.data) | 146 self.on_data(None, self.data) |
147 | 147 |
148 def on_data(self, __, data): | 148 def on_data(self, __, data): |
156 class AttachmentImagesCollectionItem(ButtonBehavior, GridLayout): | 156 class AttachmentImagesCollectionItem(ButtonBehavior, GridLayout): |
157 attachments = properties.ListProperty([]) | 157 attachments = properties.ListProperty([]) |
158 chat = properties.ObjectProperty() | 158 chat = properties.ObjectProperty() |
159 mess_data = properties.ObjectProperty() | 159 mess_data = properties.ObjectProperty() |
160 | 160 |
161 def _setPreview(self, attachment, wid, preview_path): | 161 def _set_preview(self, attachment, wid, preview_path): |
162 attachment['preview'] = preview_path | 162 attachment['preview'] = preview_path |
163 wid.source = preview_path | 163 wid.source = preview_path |
164 | 164 |
165 def _setPath(self, attachment, wid, path): | 165 def _set_path(self, attachment, wid, path): |
166 attachment['path'] = path | 166 attachment['path'] = path |
167 if wid is not None: | 167 if wid is not None: |
168 # we also need a preview for the widget | 168 # we also need a preview for the widget |
169 if 'preview' in attachment: | 169 if 'preview' in attachment: |
170 wid.source = attachment['preview'] | 170 wid.source = attachment['preview'] |
171 else: | 171 else: |
172 G.host.bridge.imageGeneratePreview( | 172 G.host.bridge.image_generate_preview( |
173 path, | 173 path, |
174 self.chat.profile, | 174 self.chat.profile, |
175 callback=partial(self._setPreview, attachment, wid), | 175 callback=partial(self._set_preview, attachment, wid), |
176 ) | 176 ) |
177 | 177 |
178 def on_kv_post(self, __): | 178 def on_kv_post(self, __): |
179 attachments = self.attachments | 179 attachments = self.attachments |
180 self.clear_widgets() | 180 self.clear_widgets() |
192 else: | 192 else: |
193 to_download = False | 193 to_download = False |
194 | 194 |
195 if idx < 3 or len(attachments) <= 4: | 195 if idx < 3 or len(attachments) <= 4: |
196 if ((self.mess_data.own_mess | 196 if ((self.mess_data.own_mess |
197 or self.chat.contact_list.isInRoster(self.mess_data.from_jid))): | 197 or self.chat.contact_list.is_in_roster(self.mess_data.from_jid))): |
198 wid = AsyncImage(size_hint=(1, 1), source="data/images/image-loading.gif") | 198 wid = AsyncImage(size_hint=(1, 1), source="data/images/image-loading.gif") |
199 if 'preview' in attachment: | 199 if 'preview' in attachment: |
200 wid.source = attachment["preview"] | 200 wid.source = attachment["preview"] |
201 elif 'path' in attachment: | 201 elif 'path' in attachment: |
202 G.host.bridge.imageGeneratePreview( | 202 G.host.bridge.image_generate_preview( |
203 attachment['path'], | 203 attachment['path'], |
204 self.chat.profile, | 204 self.chat.profile, |
205 callback=partial(self._setPreview, attachment, wid), | 205 callback=partial(self._set_preview, attachment, wid), |
206 ) | 206 ) |
207 elif url is None: | 207 elif url is None: |
208 log.warning(f"Can't find any source for {attachment}") | 208 log.warning(f"Can't find any source for {attachment}") |
209 else: | 209 else: |
210 # we'll download the file, the preview will then be generated | 210 # we'll download the file, the preview will then be generated |
218 wid = None | 218 wid = None |
219 | 219 |
220 if to_download: | 220 if to_download: |
221 # the file needs to be downloaded, the widget source, | 221 # the file needs to be downloaded, the widget source, |
222 # attachment path, and preview will then be completed | 222 # attachment path, and preview will then be completed |
223 G.host.downloadURL( | 223 G.host.download_url( |
224 url, | 224 url, |
225 callback=partial(self._setPath, attachment, wid), | 225 callback=partial(self._set_path, attachment, wid), |
226 dest=C.FILE_DEST_CACHE, | 226 dest=C.FILE_DEST_CACHE, |
227 profile=self.chat.profile, | 227 profile=self.chat.profile, |
228 ) | 228 ) |
229 | 229 |
230 if len(attachments) > 4: | 230 if len(attachments) > 4: |
241 if not source: | 241 if not source: |
242 log.warning(f"no source for {attachment}") | 242 log.warning(f"no source for {attachment}") |
243 else: | 243 else: |
244 sources.append(source) | 244 sources.append(source) |
245 gallery = ImagesGallery(sources=sources) | 245 gallery = ImagesGallery(sources=sources) |
246 G.host.showExtraUI(gallery) | 246 G.host.show_extra_ui(gallery) |
247 | 247 |
248 | 248 |
249 class AttachmentToSendItem(AttachmentItem): | 249 class AttachmentToSendItem(AttachmentItem): |
250 # True when the item is being sent | 250 # True when the item is being sent |
251 sending = properties.BooleanProperty(False) | 251 sending = properties.BooleanProperty(False) |
320 | 320 |
321 def update(self, update_dict): | 321 def update(self, update_dict): |
322 if 'avatar' in update_dict: | 322 if 'avatar' in update_dict: |
323 avatar_data = update_dict['avatar'] | 323 avatar_data = update_dict['avatar'] |
324 if avatar_data is None: | 324 if avatar_data is None: |
325 source = G.host.getDefaultAvatar() | 325 source = G.host.get_default_avatar() |
326 else: | 326 else: |
327 source = avatar_data['path'] | 327 source = avatar_data['path'] |
328 self.avatar.source = source | 328 self.avatar.source = source |
329 if 'status' in update_dict: | 329 if 'status' in update_dict: |
330 status = update_dict['status'] | 330 status = update_dict['status'] |
331 self.delivery.text = '\u2714' if status == 'delivered' else '' | 331 self.delivery.text = '\u2714' if status == 'delivered' else '' |
332 | 332 |
333 def _setPath(self, data, path): | 333 def _set_path(self, data, path): |
334 """Set path of decrypted file to an item""" | 334 """Set path of decrypted file to an item""" |
335 data['path'] = path | 335 data['path'] = path |
336 | 336 |
337 def add_attachments(self): | 337 def add_attachments(self): |
338 """Add attachments layout + attachments item""" | 338 """Add attachments layout + attachments item""" |
370 elif image_attachments: | 370 elif image_attachments: |
371 attachment = image_attachments[0] | 371 attachment = image_attachments[0] |
372 # to avoid leaking IP address, we only display image if the contact is in | 372 # to avoid leaking IP address, we only display image if the contact is in |
373 # roster | 373 # roster |
374 if ((self.mess_data.own_mess | 374 if ((self.mess_data.own_mess |
375 or self.chat.contact_list.isInRoster(self.mess_data.from_jid))): | 375 or self.chat.contact_list.is_in_roster(self.mess_data.from_jid))): |
376 try: | 376 try: |
377 url = urlparse(attachment['url']) | 377 url = urlparse(attachment['url']) |
378 except KeyError: | 378 except KeyError: |
379 item = AttachmentImageItem(data=attachment) | 379 item = AttachmentImageItem(data=attachment) |
380 else: | 380 else: |
381 if url.scheme == "aesgcm": | 381 if url.scheme == "aesgcm": |
382 # we remove the URL now, we'll replace it by | 382 # we remove the URL now, we'll replace it by |
383 # the local decrypted version | 383 # the local decrypted version |
384 del attachment['url'] | 384 del attachment['url'] |
385 item = AttachmentImageItem(data=attachment) | 385 item = AttachmentImageItem(data=attachment) |
386 G.host.downloadURL( | 386 G.host.download_url( |
387 url.geturl(), | 387 url.geturl(), |
388 callback=partial(self._setPath, item.data), | 388 callback=partial(self._set_path, item.data), |
389 dest=C.FILE_DEST_CACHE, | 389 dest=C.FILE_DEST_CACHE, |
390 profile=self.chat.profile, | 390 profile=self.chat.profile, |
391 ) | 391 ) |
392 else: | 392 else: |
393 item = AttachmentImageItem(data=attachment) | 393 item = AttachmentImageItem(data=attachment) |
430 chat = properties.ObjectProperty() | 430 chat = properties.ObjectProperty() |
431 | 431 |
432 def on_release(self, *args): | 432 def on_release(self, *args): |
433 menu.TransferMenu( | 433 menu.TransferMenu( |
434 encrypted=self.chat.encrypted, | 434 encrypted=self.chat.encrypted, |
435 callback=self.chat.transferFile, | 435 callback=self.chat.transfer_file, |
436 ).show(self) | 436 ).show(self) |
437 | 437 |
438 | 438 |
439 class ExtraMenu(DropDown): | 439 class ExtraMenu(DropDown): |
440 chat = properties.ObjectProperty() | 440 chat = properties.ObjectProperty() |
441 | 441 |
442 def on_select(self, menu): | 442 def on_select(self, menu): |
443 if menu == 'bookmark': | 443 if menu == 'bookmark': |
444 G.host.bridge.menuLaunch(C.MENU_GLOBAL, ("groups", "bookmarks"), | 444 G.host.bridge.menu_launch(C.MENU_GLOBAL, ("groups", "bookmarks"), |
445 {}, C.NO_SECURITY_LIMIT, self.chat.profile, | 445 {}, C.NO_SECURITY_LIMIT, self.chat.profile, |
446 callback=partial( | 446 callback=partial( |
447 G.host.actionManager, profile=self.chat.profile), | 447 G.host.action_manager, profile=self.chat.profile), |
448 errback=G.host.errback) | 448 errback=G.host.errback) |
449 elif menu == 'close': | 449 elif menu == 'close': |
450 if self.chat.type == C.CHAT_GROUP: | 450 if self.chat.type == C.CHAT_GROUP: |
451 # for MUC, we have to indicate the backend that we've left | 451 # for MUC, we have to indicate the backend that we've left |
452 G.host.bridge.mucLeave(self.chat.target, self.chat.profile) | 452 G.host.bridge.muc_leave(self.chat.target, self.chat.profile) |
453 else: | 453 else: |
454 # for one2one, backend doesn't keep any state, so we just delete the | 454 # for one2one, backend doesn't keep any state, so we just delete the |
455 # widget here in the frontend | 455 # widget here in the frontend |
456 G.host.widgets.deleteWidget( | 456 G.host.widgets.delete_widget( |
457 self.chat, all_instances=True, explicit_close=True) | 457 self.chat, all_instances=True, explicit_close=True) |
458 else: | 458 else: |
459 raise exceptions.InternalError("Unknown menu: {}".format(menu)) | 459 raise exceptions.InternalError("Unknown menu: {}".format(menu)) |
460 | 460 |
461 | 461 |
472 self.chat = chat | 472 self.chat = chat |
473 self.encryption_menu = EncryptionMenu(chat) | 473 self.encryption_menu = EncryptionMenu(chat) |
474 super(EncryptionMainButton, self).__init__(**kwargs) | 474 super(EncryptionMainButton, self).__init__(**kwargs) |
475 self.bind(on_release=self.encryption_menu.open) | 475 self.bind(on_release=self.encryption_menu.open) |
476 | 476 |
477 def selectAlgo(self, name): | 477 def select_algo(self, name): |
478 """Mark an encryption algorithm as selected. | 478 """Mark an encryption algorithm as selected. |
479 | 479 |
480 This will also deselect all other button | 480 This will also deselect all other button |
481 @param name(unicode, None): encryption plugin name | 481 @param name(unicode, None): encryption plugin name |
482 None for plain text | 482 None for plain text |
484 buttons = self.encryption_menu.container.children | 484 buttons = self.encryption_menu.container.children |
485 buttons[-1].selected = name is None | 485 buttons[-1].selected = name is None |
486 for button in buttons[:-1]: | 486 for button in buttons[:-1]: |
487 button.selected = button.text == name | 487 button.selected = button.text == name |
488 | 488 |
489 def getColor(self): | 489 def get_color(self): |
490 if self.chat.otr_state_encryption == OTR_STATE_UNENCRYPTED: | 490 if self.chat.otr_state_encryption == OTR_STATE_UNENCRYPTED: |
491 return (0.4, 0.4, 0.4, 1) | 491 return (0.4, 0.4, 0.4, 1) |
492 elif self.chat.otr_state_trust == OTR_STATE_TRUSTED: | 492 elif self.chat.otr_state_trust == OTR_STATE_TRUSTED: |
493 return (0.29,0.87,0.0,1) | 493 return (0.29,0.87,0.0,1) |
494 else: | 494 else: |
495 return (0.4, 0.4, 0.4, 1) | 495 return (0.4, 0.4, 0.4, 1) |
496 | 496 |
497 def getSymbol(self): | 497 def get_symbol(self): |
498 if self.chat.otr_state_encryption == OTR_STATE_UNENCRYPTED: | 498 if self.chat.otr_state_encryption == OTR_STATE_UNENCRYPTED: |
499 return 'lock-open' | 499 return 'lock-open' |
500 elif self.chat.otr_state_trust == OTR_STATE_TRUSTED: | 500 elif self.chat.otr_state_trust == OTR_STATE_TRUSTED: |
501 return 'lock-filled' | 501 return 'lock-filled' |
502 else: | 502 else: |
555 btn = EncryptionButton( | 555 btn = EncryptionButton( |
556 text=plugin['name'], | 556 text=plugin['name'], |
557 trust_button=True, | 557 trust_button=True, |
558 ) | 558 ) |
559 btn.bind( | 559 btn.bind( |
560 on_release=partial(self.startEncryption, plugin=plugin), | 560 on_release=partial(self.start_encryption, plugin=plugin), |
561 on_trust_release=partial(self.getTrustUI, plugin=plugin), | 561 on_trust_release=partial(self.get_trust_ui, plugin=plugin), |
562 ) | 562 ) |
563 self.add_widget(btn) | 563 self.add_widget(btn) |
564 log.info("added encryption: {}".format(plugin['name'])) | 564 log.info("added encryption: {}".format(plugin['name'])) |
565 | 565 |
566 def messageEncryptionStopCb(self): | 566 def message_encryption_stop_cb(self): |
567 log.info(_("Session with {destinee} is now in plain text").format( | 567 log.info(_("Session with {destinee} is now in plain text").format( |
568 destinee = self.chat.target)) | 568 destinee = self.chat.target)) |
569 | 569 |
570 def messageEncryptionStopEb(self, failure_): | 570 def message_encryption_stop_eb(self, failure_): |
571 msg = _("Error while stopping encryption with {destinee}: {reason}").format( | 571 msg = _("Error while stopping encryption with {destinee}: {reason}").format( |
572 destinee = self.chat.target, | 572 destinee = self.chat.target, |
573 reason = failure_) | 573 reason = failure_) |
574 log.warning(msg) | 574 log.warning(msg) |
575 G.host.addNote(_("encryption problem"), msg, C.XMLUI_DATA_LVL_ERROR) | 575 G.host.add_note(_("encryption problem"), msg, C.XMLUI_DATA_LVL_ERROR) |
576 | 576 |
577 def unencrypted(self, button): | 577 def unencrypted(self, button): |
578 self.dismiss() | 578 self.dismiss() |
579 G.host.bridge.messageEncryptionStop( | 579 G.host.bridge.message_encryption_stop( |
580 str(self.chat.target), | 580 str(self.chat.target), |
581 self.chat.profile, | 581 self.chat.profile, |
582 callback=self.messageEncryptionStopCb, | 582 callback=self.message_encryption_stop_cb, |
583 errback=self.messageEncryptionStopEb) | 583 errback=self.message_encryption_stop_eb) |
584 | 584 |
585 def messageEncryptionStartCb(self, plugin): | 585 def message_encryption_start_cb(self, plugin): |
586 log.info(_("Session with {destinee} is now encrypted with {encr_name}").format( | 586 log.info(_("Session with {destinee} is now encrypted with {encr_name}").format( |
587 destinee = self.chat.target, | 587 destinee = self.chat.target, |
588 encr_name = plugin['name'])) | 588 encr_name = plugin['name'])) |
589 | 589 |
590 def messageEncryptionStartEb(self, failure_): | 590 def message_encryption_start_eb(self, failure_): |
591 msg = _("Session can't be encrypted with {destinee}: {reason}").format( | 591 msg = _("Session can't be encrypted with {destinee}: {reason}").format( |
592 destinee = self.chat.target, | 592 destinee = self.chat.target, |
593 reason = failure_) | 593 reason = failure_) |
594 log.warning(msg) | 594 log.warning(msg) |
595 G.host.addNote(_("encryption problem"), msg, C.XMLUI_DATA_LVL_ERROR) | 595 G.host.add_note(_("encryption problem"), msg, C.XMLUI_DATA_LVL_ERROR) |
596 | 596 |
597 def startEncryption(self, button, plugin): | 597 def start_encryption(self, button, plugin): |
598 """Request encryption with given plugin for this session | 598 """Request encryption with given plugin for this session |
599 | 599 |
600 @param button(EncryptionButton): button which has been pressed | 600 @param button(EncryptionButton): button which has been pressed |
601 @param plugin(dict): plugin data | 601 @param plugin(dict): plugin data |
602 """ | 602 """ |
603 self.dismiss() | 603 self.dismiss() |
604 G.host.bridge.messageEncryptionStart( | 604 G.host.bridge.message_encryption_start( |
605 str(self.chat.target), | 605 str(self.chat.target), |
606 plugin['namespace'], | 606 plugin['namespace'], |
607 True, | 607 True, |
608 self.chat.profile, | 608 self.chat.profile, |
609 callback=partial(self.messageEncryptionStartCb, plugin=plugin), | 609 callback=partial(self.message_encryption_start_cb, plugin=plugin), |
610 errback=self.messageEncryptionStartEb) | 610 errback=self.message_encryption_start_eb) |
611 | 611 |
612 def encryptionTrustUIGetCb(self, xmlui_raw): | 612 def encryption_trust_ui_get_cb(self, xmlui_raw): |
613 xml_ui = xmlui.create( | 613 xml_ui = xmlui.create( |
614 G.host, xmlui_raw, profile=self.chat.profile) | 614 G.host, xmlui_raw, profile=self.chat.profile) |
615 xml_ui.show() | 615 xml_ui.show() |
616 | 616 |
617 def encryptionTrustUIGetEb(self, failure_): | 617 def encryption_trust_ui_get_eb(self, failure_): |
618 msg = _("Trust manager interface can't be retrieved: {reason}").format( | 618 msg = _("Trust manager interface can't be retrieved: {reason}").format( |
619 reason = failure_) | 619 reason = failure_) |
620 log.warning(msg) | 620 log.warning(msg) |
621 G.host.addNote(_("encryption trust management problem"), msg, | 621 G.host.add_note(_("encryption trust management problem"), msg, |
622 C.XMLUI_DATA_LVL_ERROR) | 622 C.XMLUI_DATA_LVL_ERROR) |
623 | 623 |
624 def getTrustUI(self, button, plugin): | 624 def get_trust_ui(self, button, plugin): |
625 """Request and display trust management UI | 625 """Request and display trust management UI |
626 | 626 |
627 @param button(EncryptionButton): button which has been pressed | 627 @param button(EncryptionButton): button which has been pressed |
628 @param plugin(dict): plugin data | 628 @param plugin(dict): plugin data |
629 """ | 629 """ |
630 self.dismiss() | 630 self.dismiss() |
631 G.host.bridge.encryptionTrustUIGet( | 631 G.host.bridge.encryption_trust_ui_get( |
632 str(self.chat.target), | 632 str(self.chat.target), |
633 plugin['namespace'], | 633 plugin['namespace'], |
634 self.chat.profile, | 634 self.chat.profile, |
635 callback=self.encryptionTrustUIGetCb, | 635 callback=self.encryption_trust_ui_get_cb, |
636 errback=self.encryptionTrustUIGetEb) | 636 errback=self.encryption_trust_ui_get_eb) |
637 | 637 |
638 | 638 |
639 class Chat(quick_chat.QuickChat, cagou_widget.CagouWidget): | 639 class Chat(quick_chat.QuickChat, cagou_widget.CagouWidget): |
640 message_input = properties.ObjectProperty() | 640 message_input = properties.ObjectProperty() |
641 messages_widget = properties.ObjectProperty() | 641 messages_widget = properties.ObjectProperty() |
661 self._hi_comp_last = None | 661 self._hi_comp_last = None |
662 self._hi_comp_dropdown = DropDown() | 662 self._hi_comp_dropdown = DropDown() |
663 self._hi_comp_allowed = True | 663 self._hi_comp_allowed = True |
664 cagou_widget.CagouWidget.__init__(self) | 664 cagou_widget.CagouWidget.__init__(self) |
665 transfer_btn = TransferButton(chat=self) | 665 transfer_btn = TransferButton(chat=self) |
666 self.headerInputAddExtra(transfer_btn) | 666 self.header_input_add_extra(transfer_btn) |
667 if (type_ == C.CHAT_ONE2ONE or "REALJID_PUBLIC" in statuses): | 667 if (type_ == C.CHAT_ONE2ONE or "REALJID_PUBLIC" in statuses): |
668 self.encryption_btn = EncryptionMainButton(self) | 668 self.encryption_btn = EncryptionMainButton(self) |
669 self.headerInputAddExtra(self.encryption_btn) | 669 self.header_input_add_extra(self.encryption_btn) |
670 self.extra_menu = ExtraMenu(chat=self) | 670 self.extra_menu = ExtraMenu(chat=self) |
671 extra_btn = ExtraButton(chat=self) | 671 extra_btn = ExtraButton(chat=self) |
672 self.headerInputAddExtra(extra_btn) | 672 self.header_input_add_extra(extra_btn) |
673 self.header_input.hint_text = target | 673 self.header_input.hint_text = target |
674 self._history_prepend_lock = False | 674 self._history_prepend_lock = False |
675 self.history_count = 0 | 675 self.history_count = 0 |
676 | 676 |
677 def on_kv_post(self, __): | 677 def on_kv_post(self, __): |
678 self.postInit() | 678 self.post_init() |
679 | 679 |
680 def screenManagerInit(self, screen_manager): | 680 def screen_manager_init(self, screen_manager): |
681 screen_manager.transition = screenmanager.SlideTransition(direction='down') | 681 screen_manager.transition = screenmanager.SlideTransition(direction='down') |
682 sel_screen = Screen(name='chat_selector') | 682 sel_screen = Screen(name='chat_selector') |
683 chat_selector = ChatSelector(profile=self.profile) | 683 chat_selector = ChatSelector(profile=self.profile) |
684 sel_screen.add_widget(chat_selector) | 684 sel_screen.add_widget(chat_selector) |
685 screen_manager.add_widget(sel_screen) | 685 screen_manager.add_widget(sel_screen) |
703 if target is None: | 703 if target is None: |
704 show_chat_selector = True | 704 show_chat_selector = True |
705 target = G.host.profiles[profiles[0]].whoami | 705 target = G.host.profiles[profiles[0]].whoami |
706 else: | 706 else: |
707 show_chat_selector = False | 707 show_chat_selector = False |
708 wid = G.host.widgets.getOrCreateWidget(cls, target, on_new_widget=None, | 708 wid = G.host.widgets.get_or_create_widget(cls, target, on_new_widget=None, |
709 on_existing_widget=G.host.getOrClone, | 709 on_existing_widget=G.host.get_or_clone, |
710 profiles=profiles) | 710 profiles=profiles) |
711 wid.show_chat_selector = show_chat_selector | 711 wid.show_chat_selector = show_chat_selector |
712 return wid | 712 return wid |
713 | 713 |
714 @property | 714 @property |
725 return True | 725 return True |
726 | 726 |
727 ## drop ## | 727 ## drop ## |
728 | 728 |
729 def on_drop_file(self, path): | 729 def on_drop_file(self, path): |
730 self.addAttachment(path) | 730 self.add_attachment(path) |
731 | 731 |
732 ## header ## | 732 ## header ## |
733 | 733 |
734 def changeWidget(self, jid_): | 734 def change_widget(self, jid_): |
735 """change current widget for a new one with given jid | 735 """change current widget for a new one with given jid |
736 | 736 |
737 @param jid_(jid.JID): jid of the widget to create | 737 @param jid_(jid.JID): jid of the widget to create |
738 """ | 738 """ |
739 plugin_info = G.host.getPluginInfo(main=Chat) | 739 plugin_info = G.host.get_plugin_info(main=Chat) |
740 factory = plugin_info['factory'] | 740 factory = plugin_info['factory'] |
741 G.host.switchWidget(self, factory(plugin_info, jid_, profiles=[self.profile])) | 741 G.host.switch_widget(self, factory(plugin_info, jid_, profiles=[self.profile])) |
742 self.header_input.text = '' | 742 self.header_input.text = '' |
743 | 743 |
744 def onHeaderInput(self): | 744 def on_header_wid_input(self): |
745 text = self.header_input.text.strip() | 745 text = self.header_input.text.strip() |
746 try: | 746 try: |
747 if text.count('@') != 1 or text.count(' '): | 747 if text.count('@') != 1 or text.count(' '): |
748 raise ValueError | 748 raise ValueError |
749 jid_ = jid.JID(text) | 749 jid_ = jid.JID(text) |
750 except ValueError: | 750 except ValueError: |
751 log.info("entered text is not a jid") | 751 log.info("entered text is not a jid") |
752 return | 752 return |
753 | 753 |
754 def discoCb(disco): | 754 def disco_cb(disco): |
755 # TODO: check if plugin XEP-0045 is activated | 755 # TODO: check if plugin XEP-0045 is activated |
756 if "conference" in [i[0] for i in disco[1]]: | 756 if "conference" in [i[0] for i in disco[1]]: |
757 G.host.bridge.mucJoin(str(jid_), "", "", self.profile, | 757 G.host.bridge.muc_join(str(jid_), "", "", self.profile, |
758 callback=self._mucJoinCb, errback=self._mucJoinEb) | 758 callback=self._muc_join_cb, errback=self._muc_join_eb) |
759 else: | 759 else: |
760 self.changeWidget(jid_) | 760 self.change_widget(jid_) |
761 | 761 |
762 def discoEb(failure): | 762 def disco_eb(failure): |
763 log.warning("Disco failure, ignore this text: {}".format(failure)) | 763 log.warning("Disco failure, ignore this text: {}".format(failure)) |
764 | 764 |
765 G.host.bridge.discoInfos( | 765 G.host.bridge.disco_infos( |
766 jid_.domain, | 766 jid_.domain, |
767 profile_key=self.profile, | 767 profile_key=self.profile, |
768 callback=discoCb, | 768 callback=disco_cb, |
769 errback=discoEb) | 769 errback=disco_eb) |
770 | 770 |
771 def onHeaderInputCompleted(self, input_wid, completed_text): | 771 def on_header_wid_input_completed(self, input_wid, completed_text): |
772 self._hi_comp_allowed = False | 772 self._hi_comp_allowed = False |
773 input_wid.text = completed_text | 773 input_wid.text = completed_text |
774 self._hi_comp_allowed = True | 774 self._hi_comp_allowed = True |
775 self._hi_comp_dropdown.dismiss() | 775 self._hi_comp_dropdown.dismiss() |
776 self.onHeaderInput() | 776 self.on_header_wid_input() |
777 | 777 |
778 def onHeaderInputComplete(self, wid, text): | 778 def on_header_wid_input_complete(self, wid, text): |
779 if not self._hi_comp_allowed: | 779 if not self._hi_comp_allowed: |
780 return | 780 return |
781 text = text.lstrip() | 781 text = text.lstrip() |
782 if not text: | 782 if not text: |
783 self._hi_comp_data = None | 783 self._hi_comp_data = None |
811 btn = JidButton( | 811 btn = JidButton( |
812 jid = jid_.bare, | 812 jid = jid_.bare, |
813 profile = profile, | 813 profile = profile, |
814 size_hint = (0.5, None), | 814 size_hint = (0.5, None), |
815 nick = nick, | 815 nick = nick, |
816 on_release=lambda __, txt=jid_.bare: self.onHeaderInputCompleted(wid, txt) | 816 on_release=lambda __, txt=jid_.bare: self.on_header_wid_input_completed(wid, txt) |
817 ) | 817 ) |
818 dropdown.add_widget(btn) | 818 dropdown.add_widget(btn) |
819 else: | 819 else: |
820 # more chars, we continue completion by removing unwanted widgets | 820 # more chars, we continue completion by removing unwanted widgets |
821 to_remove = [] | 821 to_remove = [] |
826 dropdown.remove_widget(c) | 826 dropdown.remove_widget(c) |
827 if dropdown.attach_to is None: | 827 if dropdown.attach_to is None: |
828 dropdown.open(wid) | 828 dropdown.open(wid) |
829 self._hi_comp_last = text | 829 self._hi_comp_last = text |
830 | 830 |
831 def messageDataConverter(self, idx, mess_id): | 831 def message_data_converter(self, idx, mess_id): |
832 return {"mess_data": self.messages[mess_id]} | 832 return {"mess_data": self.messages[mess_id]} |
833 | 833 |
834 def _onHistoryPrinted(self): | 834 def _on_history_printed(self): |
835 """Refresh or scroll down the focus after the history is printed""" | 835 """Refresh or scroll down the focus after the history is printed""" |
836 # self.adapter.data = self.messages | 836 # self.adapter.data = self.messages |
837 for mess_data in self.messages.values(): | 837 for mess_data in self.messages.values(): |
838 self.appendMessage(mess_data) | 838 self.appendMessage(mess_data) |
839 super(Chat, self)._onHistoryPrinted() | 839 super(Chat, self)._on_history_printed() |
840 | 840 |
841 def createMessage(self, message): | 841 def create_message(self, message): |
842 self.appendMessage(message) | 842 self.appendMessage(message) |
843 # we need to render immediatly next 2 layouts to avoid an unpleasant flickering | 843 # we need to render immediatly next 2 layouts to avoid an unpleasant flickering |
844 # when sending or receiving a message | 844 # when sending or receiving a message |
845 self.messages_widget.dont_delay_next_layouts = 2 | 845 self.messages_widget.dont_delay_next_layouts = 2 |
846 | 846 |
847 def appendMessage(self, mess_data): | 847 def appendMessage(self, mess_data): |
848 """Append a message Widget to the history | 848 """Append a message Widget to the history |
849 | 849 |
850 @param mess_data(quick_chat.Message): message data | 850 @param mess_data(quick_chat.Message): message data |
851 """ | 851 """ |
852 if self.handleUserMoved(mess_data): | 852 if self.handle_user_moved(mess_data): |
853 return | 853 return |
854 self.messages_widget.add_widget(MessageWidget(mess_data=mess_data)) | 854 self.messages_widget.add_widget(MessageWidget(mess_data=mess_data)) |
855 self.notify(mess_data) | 855 self.notify(mess_data) |
856 | 856 |
857 def prependMessage(self, mess_data): | 857 def prepend_message(self, mess_data): |
858 """Prepend a message Widget to the history | 858 """Prepend a message Widget to the history |
859 | 859 |
860 @param mess_data(quick_chat.Message): message data | 860 @param mess_data(quick_chat.Message): message data |
861 """ | 861 """ |
862 mess_wid = self.messages_widget | 862 mess_wid = self.messages_widget |
875 or when one2one chat is not visible. A note is also there when widget | 875 or when one2one chat is not visible. A note is also there when widget |
876 is not visible. | 876 is not visible. |
877 For group chat, note will be added on mention, with a desktop notification if | 877 For group chat, note will be added on mention, with a desktop notification if |
878 window has not focus or is not visible. | 878 window has not focus or is not visible. |
879 """ | 879 """ |
880 visible_clones = [w for w in G.host.getVisibleList(self.__class__) | 880 visible_clones = [w for w in G.host.get_visible_list(self.__class__) |
881 if w.target == self.target] | 881 if w.target == self.target] |
882 if len(visible_clones) > 1 and visible_clones.index(self) > 0: | 882 if len(visible_clones) > 1 and visible_clones.index(self) > 0: |
883 # to avoid multiple notifications in case of multiple cloned widgets | 883 # to avoid multiple notifications in case of multiple cloned widgets |
884 # we only handle first clone | 884 # we only handle first clone |
885 return | 885 return |
894 subject=_("private message"), | 894 subject=_("private message"), |
895 widget=self, | 895 widget=self, |
896 profile=self.profile | 896 profile=self.profile |
897 ) | 897 ) |
898 if not is_visible: | 898 if not is_visible: |
899 G.host.addNote( | 899 G.host.add_note( |
900 _("private message"), | 900 _("private message"), |
901 notif_msg, | 901 notif_msg, |
902 symbol = "chat", | 902 symbol = "chat", |
903 action = { | 903 action = { |
904 "action": 'chat', | 904 "action": 'chat', |
906 "profiles": self.profiles} | 906 "profiles": self.profiles} |
907 ) | 907 ) |
908 else: | 908 else: |
909 if mess_data.mention: | 909 if mess_data.mention: |
910 notif_msg = self._get_notif_msg(mess_data) | 910 notif_msg = self._get_notif_msg(mess_data) |
911 G.host.addNote( | 911 G.host.add_note( |
912 _("mention"), | 912 _("mention"), |
913 notif_msg, | 913 notif_msg, |
914 symbol = "chat", | 914 symbol = "chat", |
915 action = { | 915 action = { |
916 "action": 'chat', | 916 "action": 'chat', |
928 profile=self.profile | 928 profile=self.profile |
929 ) | 929 ) |
930 | 930 |
931 # message input | 931 # message input |
932 | 932 |
933 def _attachmentProgressCb(self, item, metadata, profile): | 933 def _attachment_progress_cb(self, item, metadata, profile): |
934 item.parent.remove_widget(item) | 934 item.parent.remove_widget(item) |
935 log.info(f"item {item.data.get('path')} uploaded successfully") | 935 log.info(f"item {item.data.get('path')} uploaded successfully") |
936 | 936 |
937 def _attachmentProgressEb(self, item, err_msg, profile): | 937 def _attachment_progress_eb(self, item, err_msg, profile): |
938 item.parent.remove_widget(item) | 938 item.parent.remove_widget(item) |
939 path = item.data.get('path') | 939 path = item.data.get('path') |
940 msg = _("item {path} could not be uploaded: {err_msg}").format( | 940 msg = _("item {path} could not be uploaded: {err_msg}").format( |
941 path=path, err_msg=err_msg) | 941 path=path, err_msg=err_msg) |
942 G.host.addNote(_("can't upload file"), msg, C.XMLUI_DATA_LVL_WARNING) | 942 G.host.add_note(_("can't upload file"), msg, C.XMLUI_DATA_LVL_WARNING) |
943 log.warning(msg) | 943 log.warning(msg) |
944 | 944 |
945 def _progressGetCb(self, item, metadata): | 945 def _progress_get_cb(self, item, metadata): |
946 try: | 946 try: |
947 position = int(metadata["position"]) | 947 position = int(metadata["position"]) |
948 size = int(metadata["size"]) | 948 size = int(metadata["size"]) |
949 except KeyError: | 949 except KeyError: |
950 # we got empty metadata, the progression is either not yet started or | 950 # we got empty metadata, the progression is either not yet started or |
958 item.progress = position/size*100 | 958 item.progress = position/size*100 |
959 | 959 |
960 if item.parent is not None: | 960 if item.parent is not None: |
961 # the item is not yet fully received, we reschedule an update | 961 # the item is not yet fully received, we reschedule an update |
962 Clock.schedule_once( | 962 Clock.schedule_once( |
963 partial(self._attachmentProgressUpdate, item), | 963 partial(self._attachment_progress_update, item), |
964 PROGRESS_UPDATE) | 964 PROGRESS_UPDATE) |
965 | 965 |
966 def _attachmentProgressUpdate(self, item, __): | 966 def _attachment_progress_update(self, item, __): |
967 G.host.bridge.progressGet( | 967 G.host.bridge.progress_get( |
968 item.data["progress_id"], | 968 item.data["progress_id"], |
969 self.profile, | 969 self.profile, |
970 callback=partial(self._progressGetCb, item), | 970 callback=partial(self._progress_get_cb, item), |
971 errback=G.host.errback, | 971 errback=G.host.errback, |
972 ) | 972 ) |
973 | 973 |
974 def addNick(self, nick): | 974 def add_nick(self, nick): |
975 """Add a nickname to message_input if suitable""" | 975 """Add a nickname to message_input if suitable""" |
976 if (self.type == C.CHAT_GROUP and not self.message_input.text.startswith(nick)): | 976 if (self.type == C.CHAT_GROUP and not self.message_input.text.startswith(nick)): |
977 self.message_input.text = f'{nick}: {self.message_input.text}' | 977 self.message_input.text = f'{nick}: {self.message_input.text}' |
978 | 978 |
979 def onSend(self, input_widget): | 979 def on_send(self, input_widget): |
980 extra = {} | 980 extra = {} |
981 for item in self.attachments_to_send.attachments.children: | 981 for item in self.attachments_to_send.attachments.children: |
982 if item.sending: | 982 if item.sending: |
983 # the item is already being sent | 983 # the item is already being sent |
984 continue | 984 continue |
997 attachment[C.KEY_ATTACHMENTS_RESIZE] = True | 997 attachment[C.KEY_ATTACHMENTS_RESIZE] = True |
998 | 998 |
999 attachments.append(attachment) | 999 attachments.append(attachment) |
1000 | 1000 |
1001 Clock.schedule_once( | 1001 Clock.schedule_once( |
1002 partial(self._attachmentProgressUpdate, item), | 1002 partial(self._attachment_progress_update, item), |
1003 PROGRESS_UPDATE) | 1003 PROGRESS_UPDATE) |
1004 | 1004 |
1005 G.host.registerProgressCbs( | 1005 G.host.register_progress_cbs( |
1006 progress_id, | 1006 progress_id, |
1007 callback=partial(self._attachmentProgressCb, item), | 1007 callback=partial(self._attachment_progress_cb, item), |
1008 errback=partial(self._attachmentProgressEb, item) | 1008 errback=partial(self._attachment_progress_eb, item) |
1009 ) | 1009 ) |
1010 | 1010 |
1011 | 1011 |
1012 G.host.messageSend( | 1012 G.host.message_send( |
1013 self.target, | 1013 self.target, |
1014 # TODO: handle language | 1014 # TODO: handle language |
1015 {'': input_widget.text}, | 1015 {'': input_widget.text}, |
1016 # TODO: put this in QuickChat | 1016 # TODO: put this in QuickChat |
1017 mess_type= | 1017 mess_type= |
1019 extra=extra, | 1019 extra=extra, |
1020 profile_key=self.profile | 1020 profile_key=self.profile |
1021 ) | 1021 ) |
1022 input_widget.text = '' | 1022 input_widget.text = '' |
1023 | 1023 |
1024 def _imageCheckCb(self, report_raw): | 1024 def _image_check_cb(self, report_raw): |
1025 report = data_format.deserialise(report_raw) | 1025 report = data_format.deserialise(report_raw) |
1026 if report['too_large']: | 1026 if report['too_large']: |
1027 self.attachments_to_send.show_resize=True | 1027 self.attachments_to_send.show_resize=True |
1028 self.attachments_to_send.reduce_checkbox.active=True | 1028 self.attachments_to_send.reduce_checkbox.active=True |
1029 | 1029 |
1030 def addAttachment(self, file_path, media_type=None): | 1030 def add_attachment(self, file_path, media_type=None): |
1031 file_path = Path(file_path) | 1031 file_path = Path(file_path) |
1032 if media_type is None: | 1032 if media_type is None: |
1033 media_type = mimetypes.guess_type(str(file_path), strict=False)[0] | 1033 media_type = mimetypes.guess_type(str(file_path), strict=False)[0] |
1034 if not self.attachments_to_send.show_resize and media_type is not None: | 1034 if not self.attachments_to_send.show_resize and media_type is not None: |
1035 # we check if the attachment is an image and if it's too large. | 1035 # we check if the attachment is an image and if it's too large. |
1036 # If too large, the reduce size check box will be displayed, and checked by | 1036 # If too large, the reduce size check box will be displayed, and checked by |
1037 # default. | 1037 # default. |
1038 main_type = media_type.split('/')[0] | 1038 main_type = media_type.split('/')[0] |
1039 if main_type == "image": | 1039 if main_type == "image": |
1040 G.host.bridge.imageCheck( | 1040 G.host.bridge.image_check( |
1041 str(file_path), | 1041 str(file_path), |
1042 callback=self._imageCheckCb, | 1042 callback=self._image_check_cb, |
1043 errback=partial( | 1043 errback=partial( |
1044 G.host.errback, | 1044 G.host.errback, |
1045 title=_("Can't check image size"), | 1045 title=_("Can't check image size"), |
1046 message=_("Can't check image at {path}: {{msg}}").format( | 1046 message=_("Can't check image at {path}: {{msg}}").format( |
1047 path=file_path), | 1047 path=file_path), |
1058 | 1058 |
1059 self.attachments_to_send.attachments.add_widget( | 1059 self.attachments_to_send.attachments.add_widget( |
1060 AttachmentToSendItem(data=data) | 1060 AttachmentToSendItem(data=data) |
1061 ) | 1061 ) |
1062 | 1062 |
1063 def transferFile(self, file_path, transfer_type=C.TRANSFER_UPLOAD, cleaning_cb=None): | 1063 def transfer_file(self, file_path, transfer_type=C.TRANSFER_UPLOAD, cleaning_cb=None): |
1064 # FIXME: cleaning_cb is not managed | 1064 # FIXME: cleaning_cb is not managed |
1065 if transfer_type == C.TRANSFER_UPLOAD: | 1065 if transfer_type == C.TRANSFER_UPLOAD: |
1066 self.addAttachment(file_path) | 1066 self.add_attachment(file_path) |
1067 elif transfer_type == C.TRANSFER_SEND: | 1067 elif transfer_type == C.TRANSFER_SEND: |
1068 if self.type == C.CHAT_GROUP: | 1068 if self.type == C.CHAT_GROUP: |
1069 log.warning("P2P transfer is not possible for group chat") | 1069 log.warning("P2P transfer is not possible for group chat") |
1070 # TODO: show an error dialog to user, or better hide the send button for | 1070 # TODO: show an error dialog to user, or better hide the send button for |
1071 # MUC | 1071 # MUC |
1072 else: | 1072 else: |
1073 jid_ = self.target | 1073 jid_ = self.target |
1074 if not jid_.resource: | 1074 if not jid_.resource: |
1075 jid_ = G.host.contact_lists[self.profile].getFullJid(jid_) | 1075 jid_ = G.host.contact_lists[self.profile].get_full_jid(jid_) |
1076 G.host.bridge.fileSend(str(jid_), str(file_path), "", "", "", | 1076 G.host.bridge.file_send(str(jid_), str(file_path), "", "", "", |
1077 profile=self.profile) | 1077 profile=self.profile) |
1078 # TODO: notification of sending/failing | 1078 # TODO: notification of sending/failing |
1079 else: | 1079 else: |
1080 raise log.error("transfer of type {} are not handled".format(transfer_type)) | 1080 raise log.error("transfer of type {} are not handled".format(transfer_type)) |
1081 | 1081 |
1082 def messageEncryptionStarted(self, plugin_data): | 1082 def message_encryption_started(self, plugin_data): |
1083 quick_chat.QuickChat.messageEncryptionStarted(self, plugin_data) | 1083 quick_chat.QuickChat.message_encryption_started(self, plugin_data) |
1084 self.encryption_btn.symbol = SYMBOL_ENCRYPTED | 1084 self.encryption_btn.symbol = SYMBOL_ENCRYPTED |
1085 self.encryption_btn.color = COLOR_ENCRYPTED | 1085 self.encryption_btn.color = COLOR_ENCRYPTED |
1086 self.encryption_btn.selectAlgo(plugin_data['name']) | 1086 self.encryption_btn.select_algo(plugin_data['name']) |
1087 | 1087 |
1088 def messageEncryptionStopped(self, plugin_data): | 1088 def message_encryption_stopped(self, plugin_data): |
1089 quick_chat.QuickChat.messageEncryptionStopped(self, plugin_data) | 1089 quick_chat.QuickChat.message_encryption_stopped(self, plugin_data) |
1090 self.encryption_btn.symbol = SYMBOL_UNENCRYPTED | 1090 self.encryption_btn.symbol = SYMBOL_UNENCRYPTED |
1091 self.encryption_btn.color = COLOR_UNENCRYPTED | 1091 self.encryption_btn.color = COLOR_UNENCRYPTED |
1092 self.encryption_btn.selectAlgo(None) | 1092 self.encryption_btn.select_algo(None) |
1093 | 1093 |
1094 def _mucJoinCb(self, joined_data): | 1094 def _muc_join_cb(self, joined_data): |
1095 joined, room_jid_s, occupants, user_nick, subject, statuses, profile = joined_data | 1095 joined, room_jid_s, occupants, user_nick, subject, statuses, profile = joined_data |
1096 self.host.mucRoomJoinedHandler(*joined_data[1:]) | 1096 self.host.muc_room_joined_handler(*joined_data[1:]) |
1097 jid_ = jid.JID(room_jid_s) | 1097 jid_ = jid.JID(room_jid_s) |
1098 self.changeWidget(jid_) | 1098 self.change_widget(jid_) |
1099 | 1099 |
1100 def _mucJoinEb(self, failure): | 1100 def _muc_join_eb(self, failure): |
1101 log.warning("Can't join room: {}".format(failure)) | 1101 log.warning("Can't join room: {}".format(failure)) |
1102 | 1102 |
1103 def onOTRState(self, state, dest_jid, profile): | 1103 def on_otr_state(self, state, dest_jid, profile): |
1104 assert profile in self.profiles | 1104 assert profile in self.profiles |
1105 if state in OTR_STATE_ENCRYPTION: | 1105 if state in OTR_STATE_ENCRYPTION: |
1106 self.otr_state_encryption = state | 1106 self.otr_state_encryption = state |
1107 elif state in OTR_STATE_TRUST: | 1107 elif state in OTR_STATE_TRUST: |
1108 self.otr_state_trust = state | 1108 self.otr_state_trust = state |
1109 else: | 1109 else: |
1110 log.error(_("Unknown OTR state received: {}".format(state))) | 1110 log.error(_("Unknown OTR state received: {}".format(state))) |
1111 return | 1111 return |
1112 self.encryption_btn.symbol = self.encryption_btn.getSymbol() | 1112 self.encryption_btn.symbol = self.encryption_btn.get_symbol() |
1113 self.encryption_btn.color = self.encryption_btn.getColor() | 1113 self.encryption_btn.color = self.encryption_btn.get_color() |
1114 | 1114 |
1115 def onVisible(self): | 1115 def on_visible(self): |
1116 if not self.sync: | 1116 if not self.sync: |
1117 self.resync() | 1117 self.resync() |
1118 | 1118 |
1119 def onSelected(self): | 1119 def on_selected(self): |
1120 G.host.clearNotifs(self.target, profile=self.profile) | 1120 G.host.clear_notifs(self.target, profile=self.profile) |
1121 | 1121 |
1122 def onDelete(self, **kwargs): | 1122 def on_delete(self, **kwargs): |
1123 if kwargs.get('explicit_close', False): | 1123 if kwargs.get('explicit_close', False): |
1124 wrapper = self.whwrapper | 1124 wrapper = self.whwrapper |
1125 if wrapper is not None: | 1125 if wrapper is not None: |
1126 if len(wrapper.carousel.slides) == 1: | 1126 if len(wrapper.carousel.slides) == 1: |
1127 # if we delete the last opened chat, we need to show the selector | 1127 # if we delete the last opened chat, we need to show the selector |
1130 screen_manager.current = 'chat_selector' | 1130 screen_manager.current = 'chat_selector' |
1131 wrapper.carousel.remove_widget(self) | 1131 wrapper.carousel.remove_widget(self) |
1132 return True | 1132 return True |
1133 # we always keep one widget, so it's available when swiping | 1133 # we always keep one widget, so it's available when swiping |
1134 # TODO: delete all widgets when chat is closed | 1134 # TODO: delete all widgets when chat is closed |
1135 nb_instances = sum(1 for _ in self.host.widgets.getWidgetInstances(self)) | 1135 nb_instances = sum(1 for _ in self.host.widgets.get_widget_instances(self)) |
1136 # we want to keep at least one instance of Chat by WHWrapper | 1136 # we want to keep at least one instance of Chat by WHWrapper |
1137 nb_to_keep = len(G.host.widgets_handler.children) | 1137 nb_to_keep = len(G.host.widgets_handler.children) |
1138 if nb_instances <= nb_to_keep: | 1138 if nb_instances <= nb_to_keep: |
1139 return False | 1139 return False |
1140 | 1140 |
1141 def _history_unlock(self, __): | 1141 def _history_unlock(self, __): |
1142 self._history_prepend_lock = False | 1142 self._history_prepend_lock = False |
1143 log.debug("history prepend unlocked") | 1143 log.debug("history prepend unlocked") |
1144 # we call manually onScroll, to check if we are still in the scrolling zone | 1144 # we call manually on_scroll, to check if we are still in the scrolling zone |
1145 self.onScroll(self.history_scroll, self.history_scroll.scroll_y) | 1145 self.on_scroll(self.history_scroll, self.history_scroll.scroll_y) |
1146 | 1146 |
1147 def _history_scroll_adjust(self, __, scroll_start_height): | 1147 def _history_scroll_adjust(self, __, scroll_start_height): |
1148 # history scroll position must correspond to where it was before new messages | 1148 # history scroll position must correspond to where it was before new messages |
1149 # have been appended | 1149 # have been appended |
1150 self.history_scroll.scroll_y = ( | 1150 self.history_scroll.scroll_y = ( |
1153 | 1153 |
1154 # we want a small delay before unlocking, to avoid re-fetching history | 1154 # we want a small delay before unlocking, to avoid re-fetching history |
1155 # again | 1155 # again |
1156 Clock.schedule_once(self._history_unlock, 1.5) | 1156 Clock.schedule_once(self._history_unlock, 1.5) |
1157 | 1157 |
1158 def _backHistoryGetCb_post(self, __, history, scroll_start_height): | 1158 def _back_history_get_cb_post(self, __, history, scroll_start_height): |
1159 if len(history) == 0: | 1159 if len(history) == 0: |
1160 # we don't unlock self._history_prepend_lock if there is no history, as there | 1160 # we don't unlock self._history_prepend_lock if there is no history, as there |
1161 # is no sense to try to retrieve more in this case. | 1161 # is no sense to try to retrieve more in this case. |
1162 log.debug(f"we've reached top of history for {self.target.bare} chat") | 1162 log.debug(f"we've reached top of history for {self.target.bare} chat") |
1163 else: | 1163 else: |
1168 self._history_scroll_adjust, | 1168 self._history_scroll_adjust, |
1169 scroll_start_height=scroll_start_height)) | 1169 scroll_start_height=scroll_start_height)) |
1170 log.debug( | 1170 log.debug( |
1171 f"{len(history)} messages prepended to history (last: {history[0][0]})") | 1171 f"{len(history)} messages prepended to history (last: {history[0][0]})") |
1172 | 1172 |
1173 def _backHistoryGetCb(self, history): | 1173 def _back_history_get_cb(self, history): |
1174 # TODO: factorise with QuickChat._historyGetCb | 1174 # TODO: factorise with QuickChat._history_get_cb |
1175 scroll_start_height = self.messages_widget.height * self.history_scroll.scroll_y | 1175 scroll_start_height = self.messages_widget.height * self.history_scroll.scroll_y |
1176 for data in reversed(history): | 1176 for data in reversed(history): |
1177 uid, timestamp, from_jid, to_jid, message, subject, type_, extra_s = data | 1177 uid, timestamp, from_jid, to_jid, message, subject, type_, extra_s = data |
1178 from_jid = jid.JID(from_jid) | 1178 from_jid = jid.JID(from_jid) |
1179 to_jid = jid.JID(to_jid) | 1179 to_jid = jid.JID(to_jid) |
1190 type_, | 1190 type_, |
1191 extra, | 1191 extra, |
1192 self.profile, | 1192 self.profile, |
1193 ) | 1193 ) |
1194 self.messages.move_to_end(uid, last=False) | 1194 self.messages.move_to_end(uid, last=False) |
1195 self.prependMessage(message) | 1195 self.prepend_message(message) |
1196 Clock.schedule_once(partial( | 1196 Clock.schedule_once(partial( |
1197 self._backHistoryGetCb_post, | 1197 self._back_history_get_cb_post, |
1198 history=history, | 1198 history=history, |
1199 scroll_start_height=scroll_start_height)) | 1199 scroll_start_height=scroll_start_height)) |
1200 | 1200 |
1201 def _backHistoryGetEb(self, failure_): | 1201 def _back_history_get_eb(self, failure_): |
1202 G.host.addNote( | 1202 G.host.add_note( |
1203 _("Problem while getting back history"), | 1203 _("Problem while getting back history"), |
1204 _("Can't back history for {target}: {problem}").format( | 1204 _("Can't back history for {target}: {problem}").format( |
1205 target=self.target, problem=failure_), | 1205 target=self.target, problem=failure_), |
1206 C.XMLUI_DATA_LVL_ERROR) | 1206 C.XMLUI_DATA_LVL_ERROR) |
1207 # we don't unlock self._history_prepend_lock on purpose, no need | 1207 # we don't unlock self._history_prepend_lock on purpose, no need |
1208 # to try to get more history if something is wrong | 1208 # to try to get more history if something is wrong |
1209 | 1209 |
1210 def onScroll(self, scroll_view, scroll_y): | 1210 def on_scroll(self, scroll_view, scroll_y): |
1211 if self._history_prepend_lock: | 1211 if self._history_prepend_lock: |
1212 return | 1212 return |
1213 if (1-scroll_y) * self.messages_widget.height < INFINITE_SCROLL_LIMIT: | 1213 if (1-scroll_y) * self.messages_widget.height < INFINITE_SCROLL_LIMIT: |
1214 self._history_prepend_lock = True | 1214 self._history_prepend_lock = True |
1215 log.debug(f"Retrieving back history for {self} [{self.history_count}]") | 1215 log.debug(f"Retrieving back history for {self} [{self.history_count}]") |
1216 self.history_count += 1 | 1216 self.history_count += 1 |
1217 first_uid = next(iter(self.messages.keys())) | 1217 first_uid = next(iter(self.messages.keys())) |
1218 filters = self.history_filters.copy() | 1218 filters = self.history_filters.copy() |
1219 filters['before_uid'] = first_uid | 1219 filters['before_uid'] = first_uid |
1220 self.host.bridge.historyGet( | 1220 self.host.bridge.history_get( |
1221 str(self.host.profiles[self.profile].whoami.bare), | 1221 str(self.host.profiles[self.profile].whoami.bare), |
1222 str(self.target), | 1222 str(self.target), |
1223 30, | 1223 30, |
1224 True, | 1224 True, |
1225 {k: str(v) for k,v in filters.items()}, | 1225 {k: str(v) for k,v in filters.items()}, |
1226 self.profile, | 1226 self.profile, |
1227 callback=self._backHistoryGetCb, | 1227 callback=self._back_history_get_cb, |
1228 errback=self._backHistoryGetEb, | 1228 errback=self._back_history_get_eb, |
1229 ) | 1229 ) |
1230 | 1230 |
1231 | 1231 |
1232 class ChatSelector(cagou_widget.CagouWidget, FilterBehavior): | 1232 class ChatSelector(cagou_widget.CagouWidget, FilterBehavior): |
1233 jid_selector = properties.ObjectProperty() | 1233 jid_selector = properties.ObjectProperty() |
1235 plugin_info_class = Chat | 1235 plugin_info_class = Chat |
1236 use_header_input = True | 1236 use_header_input = True |
1237 | 1237 |
1238 def on_select(self, contact_button): | 1238 def on_select(self, contact_button): |
1239 contact_jid = jid.JID(contact_button.jid) | 1239 contact_jid = jid.JID(contact_button.jid) |
1240 plugin_info = G.host.getPluginInfo(main=Chat) | 1240 plugin_info = G.host.get_plugin_info(main=Chat) |
1241 factory = plugin_info['factory'] | 1241 factory = plugin_info['factory'] |
1242 self.screen_manager.transition.direction = 'up' | 1242 self.screen_manager.transition.direction = 'up' |
1243 carousel = self.whwrapper.carousel | 1243 carousel = self.whwrapper.carousel |
1244 current_slides = {w.target: w for w in carousel.slides} | 1244 current_slides = {w.target: w for w in carousel.slides} |
1245 if contact_jid in current_slides: | 1245 if contact_jid in current_slides: |
1246 slide = current_slides[contact_jid] | 1246 slide = current_slides[contact_jid] |
1247 idx = carousel.slides.index(slide) | 1247 idx = carousel.slides.index(slide) |
1248 carousel.index = idx | 1248 carousel.index = idx |
1249 self.screen_manager.current = '' | 1249 self.screen_manager.current = '' |
1250 else: | 1250 else: |
1251 G.host.switchWidget( | 1251 G.host.switch_widget( |
1252 self, factory(plugin_info, contact_jid, profiles=[self.profile])) | 1252 self, factory(plugin_info, contact_jid, profiles=[self.profile])) |
1253 | 1253 |
1254 | 1254 |
1255 def onHeaderInput(self): | 1255 def on_header_wid_input(self): |
1256 text = self.header_input.text.strip() | 1256 text = self.header_input.text.strip() |
1257 try: | 1257 try: |
1258 if text.count('@') != 1 or text.count(' '): | 1258 if text.count('@') != 1 or text.count(' '): |
1259 raise ValueError | 1259 raise ValueError |
1260 jid_ = jid.JID(text) | 1260 jid_ = jid.JID(text) |
1261 except ValueError: | 1261 except ValueError: |
1262 log.info("entered text is not a jid") | 1262 log.info("entered text is not a jid") |
1263 return | 1263 return |
1264 G.host.doAction("chat", jid_, [self.profile]) | 1264 G.host.do_action("chat", jid_, [self.profile]) |
1265 | 1265 |
1266 def onHeaderInputComplete(self, wid, text, **kwargs): | 1266 def on_header_wid_input_complete(self, wid, text, **kwargs): |
1267 """we filter items when text is entered in input box""" | 1267 """we filter items when text is entered in input box""" |
1268 for layout in self.jid_selector.items_layouts: | 1268 for layout in self.jid_selector.items_layouts: |
1269 self.do_filter( | 1269 self.do_filter( |
1270 layout, | 1270 layout, |
1271 text, | 1271 text, |