Mercurial > libervia-desktop-kivy
comparison cagou/plugins/plugin_wid_chat.py @ 326:d9d2b56f46db
plugin chat: infinite scroll:
each when use scroll on top of current history, 30 new messages are prepended.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 06 Dec 2019 13:25:33 +0100 |
parents | 5868a5575e01 |
children | 51520ce98154 |
comparison
equal
deleted
inserted
replaced
325:5868a5575e01 | 326:d9d2b56f46db |
---|---|
38 from cagou.core import cagou_widget | 38 from cagou.core import cagou_widget |
39 from cagou.core import xmlui | 39 from cagou.core import xmlui |
40 from cagou.core.image import Image | 40 from cagou.core.image import Image |
41 from cagou.core.common import SymbolButton, JidButton | 41 from cagou.core.common import SymbolButton, JidButton |
42 from cagou.core import menu | 42 from cagou.core import menu |
43 # from random import randrange | |
44 | 43 |
45 log = logging.getLogger(__name__) | 44 log = logging.getLogger(__name__) |
46 | 45 |
47 PLUGIN_INFO = { | 46 PLUGIN_INFO = { |
48 "name": _("chat"), | 47 "name": _("chat"), |
64 SYMBOL_ENCRYPTED = 'lock' | 63 SYMBOL_ENCRYPTED = 'lock' |
65 SYMBOL_ENCRYPTED_TRUSTED = 'lock-filled' | 64 SYMBOL_ENCRYPTED_TRUSTED = 'lock-filled' |
66 COLOR_UNENCRYPTED = (0.4, 0.4, 0.4, 1) | 65 COLOR_UNENCRYPTED = (0.4, 0.4, 0.4, 1) |
67 COLOR_ENCRYPTED = (0.4, 0.4, 0.4, 1) | 66 COLOR_ENCRYPTED = (0.4, 0.4, 0.4, 1) |
68 COLOR_ENCRYPTED_TRUSTED = (0.29,0.87,0.0,1) | 67 COLOR_ENCRYPTED_TRUSTED = (0.29,0.87,0.0,1) |
68 | |
69 # below this limit, new messages will be prepended | |
70 INFINITE_SCROLL_LIMIT = dp(600) | |
69 | 71 |
70 | 72 |
71 class MessAvatar(Image): | 73 class MessAvatar(Image): |
72 pass | 74 pass |
73 | 75 |
399 | 401 |
400 | 402 |
401 class Chat(quick_chat.QuickChat, cagou_widget.CagouWidget): | 403 class Chat(quick_chat.QuickChat, cagou_widget.CagouWidget): |
402 message_input = properties.ObjectProperty() | 404 message_input = properties.ObjectProperty() |
403 messages_widget = properties.ObjectProperty() | 405 messages_widget = properties.ObjectProperty() |
406 history_scroll = properties.ObjectProperty() | |
404 | 407 |
405 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, nick=None, occupants=None, | 408 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, nick=None, occupants=None, |
406 subject=None, profiles=None): | 409 subject=None, profiles=None): |
407 quick_chat.QuickChat.__init__( | 410 quick_chat.QuickChat.__init__( |
408 self, host, target, type_, nick, occupants, subject, profiles=profiles) | 411 self, host, target, type_, nick, occupants, subject, profiles=profiles) |
421 self.headerInputAddExtra(self.encryption_btn) | 424 self.headerInputAddExtra(self.encryption_btn) |
422 self.extra_menu = ExtraMenu(chat=self) | 425 self.extra_menu = ExtraMenu(chat=self) |
423 extra_btn = ExtraButton(chat=self) | 426 extra_btn = ExtraButton(chat=self) |
424 self.headerInputAddExtra(extra_btn) | 427 self.headerInputAddExtra(extra_btn) |
425 self.header_input.hint_text = target | 428 self.header_input.hint_text = target |
429 self._history_prepend_lock = False | |
426 Clock.schedule_once(lambda dt: self.postInit(), 0) | 430 Clock.schedule_once(lambda dt: self.postInit(), 0) |
431 self.history_count = 0 | |
427 | 432 |
428 def __str__(self): | 433 def __str__(self): |
429 return "Chat({})".format(self.target) | 434 return "Chat({})".format(self.target) |
430 | 435 |
431 def __repr__(self): | 436 def __repr__(self): |
562 """ | 567 """ |
563 if self.handleUserMoved(mess_data): | 568 if self.handleUserMoved(mess_data): |
564 return | 569 return |
565 self.messages_widget.add_widget(MessageWidget(mess_data=mess_data)) | 570 self.messages_widget.add_widget(MessageWidget(mess_data=mess_data)) |
566 self.notify(mess_data) | 571 self.notify(mess_data) |
572 | |
573 def prependMessage(self, mess_data): | |
574 """Prepend a message Widget to the history | |
575 | |
576 @param mess_data(quick_chat.Message): message data | |
577 """ | |
578 mess_wid = self.messages_widget | |
579 last_idx = len(mess_wid.children) | |
580 mess_wid.add_widget(MessageWidget(mess_data=mess_data), index=last_idx) | |
567 | 581 |
568 def _get_notif_msg(self, mess_data): | 582 def _get_notif_msg(self, mess_data): |
569 return _("{nick}: {message}").format( | 583 return _("{nick}: {message}").format( |
570 nick=mess_data.nick, | 584 nick=mess_data.nick, |
571 message=mess_data.main_message) | 585 message=mess_data.main_message) |
740 if nb_instances > 1: | 754 if nb_instances > 1: |
741 return super(Chat, self).onDelete() | 755 return super(Chat, self).onDelete() |
742 else: | 756 else: |
743 return False | 757 return False |
744 | 758 |
759 def _history_unlock(self, __): | |
760 self._history_prepend_lock = False | |
761 log.debug("history prepend unlocked") | |
762 # we call manually onScroll, to check if we are still in the scrolling zone | |
763 self.onScroll(self.history_scroll, self.history_scroll.scroll_y) | |
764 | |
765 def _history_scroll_adjust(self, __, scroll_start_height): | |
766 # history scroll position must correspond to where it was before new messages | |
767 # have been appended | |
768 self.history_scroll.scroll_y = ( | |
769 scroll_start_height / self.messages_widget.height | |
770 ) | |
771 | |
772 # we want a small delay before unlocking, to avoid re-fetching history | |
773 # again | |
774 Clock.schedule_once(self._history_unlock, 1.5) | |
775 | |
776 def _backHistoryGetCb_post(self, __, history, scroll_start_height): | |
777 if len(history) == 0: | |
778 # we don't unlock self._history_prepend_lock if there is no history, as there | |
779 # is no sense to try to retrieve more in this case. | |
780 log.debug(f"we've reached top of history for {self.target.bare} chat") | |
781 else: | |
782 # we have to schedule again for _history_scroll_adjust, else messages_widget | |
783 # is not resized (self.messages_widget.height is not yet updated) | |
784 # as a result, the scroll_to can't work correctly | |
785 Clock.schedule_once(partial( | |
786 self._history_scroll_adjust, | |
787 scroll_start_height=scroll_start_height)) | |
788 log.debug( | |
789 f"{len(history)} messages prepended to history (last: {history[0][0]})") | |
790 | |
791 def _backHistoryGetCb(self, history): | |
792 # TODO: factorise with QuickChat._historyGetCb | |
793 scroll_start_height = self.messages_widget.height * self.history_scroll.scroll_y | |
794 for data in reversed(history): | |
795 uid, timestamp, from_jid, to_jid, message, subject, type_, extra = data | |
796 from_jid = jid.JID(from_jid) | |
797 to_jid = jid.JID(to_jid) | |
798 extra["history"] = True | |
799 self.messages[uid] = message = quick_chat.Message( | |
800 self, | |
801 uid, | |
802 timestamp, | |
803 from_jid, | |
804 to_jid, | |
805 message, | |
806 subject, | |
807 type_, | |
808 extra, | |
809 self.profile, | |
810 ) | |
811 self.messages.move_to_end(uid, last=False) | |
812 self.prependMessage(message) | |
813 Clock.schedule_once(partial( | |
814 self._backHistoryGetCb_post, | |
815 history=history, | |
816 scroll_start_height=scroll_start_height)) | |
817 | |
818 def _backHistoryGetEb(self, failure_): | |
819 G.host.addNote( | |
820 _("Problem while getting back history"), | |
821 _("Can't back history for {target}: {problem}").format( | |
822 target=self.target, problem=failure_), | |
823 C.XMLUI_DATA_LVL_ERROR) | |
824 # we don't unlock self._history_prepend_lock on purpose, no need | |
825 # to try to get more history if something is wrong | |
826 | |
827 def onScroll(self, scroll_view, scroll_y): | |
828 if self._history_prepend_lock: | |
829 return | |
830 if (1-scroll_y) * self.messages_widget.height < INFINITE_SCROLL_LIMIT: | |
831 self._history_prepend_lock = True | |
832 log.debug(f"Retrieving back history for {self} [{self.history_count}]") | |
833 self.history_count += 1 | |
834 first_uid = next(iter(self.messages.keys())) | |
835 filters = self.history_filters.copy() | |
836 filters['before_uid'] = first_uid | |
837 self.host.bridge.historyGet( | |
838 str(self.host.profiles[self.profile].whoami.bare), | |
839 str(self.target), | |
840 30, | |
841 True, | |
842 {k: str(v) for k,v in filters.items()}, | |
843 self.profile, | |
844 callback=self._backHistoryGetCb, | |
845 errback=self._backHistoryGetEb, | |
846 ) | |
847 | |
745 | 848 |
746 PLUGIN_INFO["factory"] = Chat.factory | 849 PLUGIN_INFO["factory"] = Chat.factory |
747 quick_widgets.register(quick_chat.QuickChat, Chat) | 850 quick_widgets.register(quick_chat.QuickChat, Chat) |