Philipp Hörist pushed to branch sort-chat-list at gajim / gajim
Commits:
-
909bae91
by mjk at 2023-05-13T13:45:12+00:00
-
ff08ac13
by mjk at 2023-05-13T13:54:14+00:00
-
63eb1041
by wurstsalat at 2023-05-13T22:33:58+02:00
-
1128a3db
by Philipp Hörist at 2023-05-14T13:37:45+02:00
-
e205b303
by Philipp Hörist at 2023-05-14T11:38:57+00:00
9 changed files:
- gajim/common/modules/mam.py
- gajim/data/gui/advanced_configuration.ui
- gajim/data/gui/groupchat_details.ui
- gajim/gtk/advanced_config.py
- gajim/gtk/chat_list.py
- gajim/gtk/chat_list_row.py
- gajim/gtk/chat_stack.py
- gajim/gtk/const.py
- gajim/gtk/groupchat_details.py
Changes:
| ... | ... | @@ -464,12 +464,16 @@ def request_archive_on_muc_join(self, jid: JID): |
| 464 | 464 | if threshold == SyncThreshold.NO_SYNC:
|
| 465 | 465 | return
|
| 466 | 466 | |
| 467 | + contact = self._get_contact(jid, groupchat=True)
|
|
| 468 | + contact.notify('mam-sync-started')
|
|
| 469 | + |
|
| 467 | 470 | mam_id, start_date = self._get_muc_query_params(jid, threshold)
|
| 468 | 471 | |
| 469 | 472 | result = yield self._execute_query(jid, mam_id, start_date)
|
| 470 | 473 | if is_error(result):
|
| 471 | 474 | if result.condition != 'item-not-found':
|
| 472 | 475 | self._log.warning(result)
|
| 476 | + contact.notify('mam-sync-error', result.get_text())
|
|
| 473 | 477 | return
|
| 474 | 478 | |
| 475 | 479 | app.storage.archive.reset_archive_infos(result.jid)
|
| ... | ... | @@ -477,6 +481,7 @@ def request_archive_on_muc_join(self, jid: JID): |
| 477 | 481 | result = yield self._execute_query(result.jid, None, start_date)
|
| 478 | 482 | if is_error(result):
|
| 479 | 483 | self._log.warning(result)
|
| 484 | + contact.notify('mam-sync-error', result.get_text())
|
|
| 480 | 485 | return
|
| 481 | 486 | |
| 482 | 487 | if result.rsm.last is not None:
|
| ... | ... | @@ -488,6 +493,8 @@ def request_archive_on_muc_join(self, jid: JID): |
| 488 | 493 | last_mam_id=result.rsm.last,
|
| 489 | 494 | last_muc_timestamp=time.time())
|
| 490 | 495 | |
| 496 | + contact.notify('mam-sync-finished')
|
|
| 497 | + |
|
| 491 | 498 | @as_task
|
| 492 | 499 | def _execute_query(self,
|
| 493 | 500 | jid: JID,
|
| ... | ... | @@ -96,7 +96,7 @@ |
| 96 | 96 | <object class="GtkLabel" id="description">
|
| 97 | 97 | <property name="height_request">40</property>
|
| 98 | 98 | <property name="visible">True</property>
|
| 99 | - <property name="can_focus">False</property>
|
|
| 99 | + <property name="selectable">True</property>
|
|
| 100 | 100 | <property name="wrap">True</property>
|
| 101 | 101 | <property name="max_width_chars">60</property>
|
| 102 | 102 | <property name="xalign">0</property>
|
| ... | ... | @@ -55,9 +55,10 @@ |
| 55 | 55 | <child>
|
| 56 | 56 | <object class="GtkToggleButton" id="edit_name_button">
|
| 57 | 57 | <property name="visible">True</property>
|
| 58 | + <property name="sensitive">False</property>
|
|
| 58 | 59 | <property name="can-focus">True</property>
|
| 59 | 60 | <property name="receives-default">True</property>
|
| 60 | - <property name="tooltip-text" translatable="yes">Edit Name…</property>
|
|
| 61 | + <property name="tooltip-text" translatable="yes">Not connected</property>
|
|
| 61 | 62 | <property name="valign">center</property>
|
| 62 | 63 | <signal name="toggled" handler="_on_edit_name_toggled" swapped="no"/>
|
| 63 | 64 | <child>
|
| ... | ... | @@ -201,7 +201,10 @@ def _on_config_edited(self, |
| 201 | 201 | |
| 202 | 202 | value = text
|
| 203 | 203 | if modelrow[Column.TYPE] == SETTING_TYPES[int]:
|
| 204 | - value = int(text)
|
|
| 204 | + try:
|
|
| 205 | + value = int(text)
|
|
| 206 | + except ValueError:
|
|
| 207 | + return
|
|
| 205 | 208 | |
| 206 | 209 | app.settings.set(setting, value)
|
| 207 | 210 | modelrow[Column.VALUE] = text
|
| ... | ... | @@ -71,7 +71,9 @@ def __init__(self, workspace_id: str) -> None: |
| 71 | 71 | self.set_sort_func(self._sort_func)
|
| 72 | 72 | self._set_placeholder()
|
| 73 | 73 | |
| 74 | - self._mouseover: bool = False
|
|
| 74 | + self._rows_need_sort = False
|
|
| 75 | + self._context_menu_visible = False
|
|
| 76 | + self._mouseover = False
|
|
| 75 | 77 | self._pinned_order_change = False
|
| 76 | 78 | self.connect('enter-notify-event', self._on_mouse_focus_changed)
|
| 77 | 79 | self.connect('leave-notify-event', self._on_mouse_focus_changed)
|
| ... | ... | @@ -206,6 +208,8 @@ def add_chat(self, |
| 206 | 208 | |
| 207 | 209 | row.connect('drag-begin', self._on_row_drag_begin)
|
| 208 | 210 | row.connect('unread-changed', self._on_row_unread_changed)
|
| 211 | + row.connect('context-menu-state-changed',
|
|
| 212 | + self._on_context_menu_state_changed)
|
|
| 209 | 213 | |
| 210 | 214 | self.add(row)
|
| 211 | 215 | |
| ... | ... | @@ -371,6 +375,14 @@ def _on_row_drag_begin(self, |
| 371 | 375 | def _on_row_unread_changed(self, row: ChatListRow) -> None:
|
| 372 | 376 | self._emit_unread_changed()
|
| 373 | 377 | |
| 378 | + def _on_context_menu_state_changed(self,
|
|
| 379 | + row: ChatListRow,
|
|
| 380 | + menu_is_visible: bool
|
|
| 381 | + ) -> None:
|
|
| 382 | + |
|
| 383 | + self._context_menu_visible = menu_is_visible
|
|
| 384 | + self._schedule_check_sort_inhibit()
|
|
| 385 | + |
|
| 374 | 386 | def _on_drag_data_received(self,
|
| 375 | 387 | _widget: Gtk.Widget,
|
| 376 | 388 | _drag_context: Gdk.DragContext,
|
| ... | ... | @@ -478,8 +490,9 @@ def _header_func(row: ChatListRow, before: ChatListRow | None) -> None: |
| 478 | 490 | row.set_header_type(None)
|
| 479 | 491 | |
| 480 | 492 | def _sort_func(self, row1: ChatListRow, row2: ChatListRow) -> int:
|
| 481 | - if self._mouseover and not self._pinned_order_change:
|
|
| 482 | - log.debug('Mouseover active, don’t sort rows')
|
|
| 493 | + if self._is_sort_inhibited():
|
|
| 494 | + self._rows_need_sort = True
|
|
| 495 | + log.debug('Sort inhibited')
|
|
| 483 | 496 | return 0
|
| 484 | 497 | |
| 485 | 498 | # Sort pinned rows according to stored order
|
| ... | ... | @@ -497,6 +510,33 @@ def _sort_func(self, row1: ChatListRow, row2: ChatListRow) -> int: |
| 497 | 510 | # Sort by timestamp
|
| 498 | 511 | return -1 if row1.timestamp > row2.timestamp else 1
|
| 499 | 512 | |
| 513 | + def invalidate_sort(self) -> None:
|
|
| 514 | + if self._is_sort_inhibited():
|
|
| 515 | + return
|
|
| 516 | + |
|
| 517 | + self._rows_need_sort = False
|
|
| 518 | + log.debug('Sort Chatlist')
|
|
| 519 | + Gtk.ListBox.invalidate_sort(self)
|
|
| 520 | + |
|
| 521 | + def _is_sort_inhibited(self) -> bool:
|
|
| 522 | + if self._pinned_order_change:
|
|
| 523 | + return False
|
|
| 524 | + return self._mouseover or self._context_menu_visible
|
|
| 525 | + |
|
| 526 | + def _schedule_check_sort_inhibit(self) -> None:
|
|
| 527 | + if self._is_sort_inhibited():
|
|
| 528 | + return
|
|
| 529 | + GLib.timeout_add(100, self._check_sort_inhibit)
|
|
| 530 | + |
|
| 531 | + def _check_sort_inhibit(self) -> None:
|
|
| 532 | + if self._is_sort_inhibited():
|
|
| 533 | + return
|
|
| 534 | + |
|
| 535 | + if not self._rows_need_sort:
|
|
| 536 | + return
|
|
| 537 | + |
|
| 538 | + self.invalidate_sort()
|
|
| 539 | + |
|
| 500 | 540 | def _on_mouse_focus_changed(self,
|
| 501 | 541 | _widget: Gtk.ListBox,
|
| 502 | 542 | event: Gdk.EventCrossing
|
| ... | ... | @@ -510,6 +550,8 @@ def _on_mouse_focus_changed(self, |
| 510 | 550 | # Not hovering a Gtk.ListBoxRow (row is INFERIOR)
|
| 511 | 551 | self._mouseover = False
|
| 512 | 552 | |
| 553 | + self._schedule_check_sort_inhibit()
|
|
| 554 | + |
|
| 513 | 555 | @staticmethod
|
| 514 | 556 | def _on_start_chat_clicked(_button: Gtk.Button) -> None:
|
| 515 | 557 | app.app.activate_action('start-chat', GLib.Variant('as', ['', '']))
|
| ... | ... | @@ -63,6 +63,10 @@ class ChatListRow(Gtk.ListBoxRow): |
| 63 | 63 | GObject.SignalFlags.RUN_LAST,
|
| 64 | 64 | None,
|
| 65 | 65 | ()),
|
| 66 | + 'context-menu-state-changed': (
|
|
| 67 | + GObject.SignalFlags.RUN_LAST,
|
|
| 68 | + None,
|
|
| 69 | + (bool,)),
|
|
| 66 | 70 | }
|
| 67 | 71 | |
| 68 | 72 | def __init__(self,
|
| ... | ... | @@ -437,14 +441,14 @@ def _on_row_button_press_event(self, |
| 437 | 441 | ) -> None:
|
| 438 | 442 | |
| 439 | 443 | if event.button == Gdk.BUTTON_SECONDARY:
|
| 440 | - self._popup_menu(event)
|
|
| 444 | + self._raise_context_popover(event)
|
|
| 441 | 445 | |
| 442 | 446 | elif event.button == Gdk.BUTTON_MIDDLE:
|
| 443 | 447 | app.window.activate_action(
|
| 444 | 448 | 'remove-chat',
|
| 445 | 449 | GLib.Variant('as', [self.account, str(self.jid)]))
|
| 446 | 450 | |
| 447 | - def _popup_menu(self, event: Gdk.EventButton):
|
|
| 451 | + def _raise_context_popover(self, event: Gdk.EventButton):
|
|
| 448 | 452 | menu = get_chat_list_row_menu(
|
| 449 | 453 | self.workspace_id, self.account, self.jid, self._pinned)
|
| 450 | 454 | |
| ... | ... | @@ -460,8 +464,13 @@ def _popup_menu(self, event: Gdk.EventButton): |
| 460 | 464 | |
| 461 | 465 | popover = GajimPopover(menu, relative_to=self)
|
| 462 | 466 | popover.set_pointing_to_coord(x=x, y=event.y)
|
| 467 | + popover.connect('closed', self._on_context_popover_closed)
|
|
| 468 | + self.emit('context-menu-state-changed', True)
|
|
| 463 | 469 | popover.popup()
|
| 464 | 470 | |
| 471 | + def _on_context_popover_closed(self, _popover: Gtk.Popover) -> None:
|
|
| 472 | + self.emit('context-menu-state-changed', False)
|
|
| 473 | + |
|
| 465 | 474 | def _on_drag_begin(self,
|
| 466 | 475 | row: ChatListRow,
|
| 467 | 476 | drag_context: Gdk.DragContext
|
| ... | ... | @@ -507,7 +507,6 @@ def _update_base_actions(self, contact: ChatContactT) -> None: |
| 507 | 507 | jingle.get_enabled() or
|
| 508 | 508 | httpupload.get_enabled())
|
| 509 | 509 | |
| 510 | - app.window.get_action('show-contact-info').set_enabled(online)
|
|
| 511 | 510 | app.window.get_action('correct-message').set_enabled(online)
|
| 512 | 511 | |
| 513 | 512 | def _update_chat_actions(self, contact: BareContact) -> None:
|
| ... | ... | @@ -232,7 +232,7 @@ def __str__(self): |
| 232 | 232 | ('add-to-roster', None, True),
|
| 233 | 233 | ('start-voice-call', None, False),
|
| 234 | 234 | ('start-video-call', None, False),
|
| 235 | - ('show-contact-info', None, False),
|
|
| 235 | + ('show-contact-info', None, True),
|
|
| 236 | 236 | ('send-message', None, False),
|
| 237 | 237 | ('muc-change-nickname', None, False),
|
| 238 | 238 | ('muc-invite', None, False),
|
| ... | ... | @@ -22,7 +22,9 @@ |
| 22 | 22 | |
| 23 | 23 | from gajim.common import app
|
| 24 | 24 | from gajim.common import ged
|
| 25 | +from gajim.common import types
|
|
| 25 | 26 | from gajim.common.const import AvatarSize
|
| 27 | +from gajim.common.const import SimpleClientState
|
|
| 26 | 28 | from gajim.common.events import MucDiscoUpdate
|
| 27 | 29 | from gajim.common.i18n import _
|
| 28 | 30 | from gajim.common.modules.contacts import GroupchatContact
|
| ... | ... | @@ -55,6 +57,9 @@ def __init__(self, |
| 55 | 57 | |
| 56 | 58 | self.account = contact.account
|
| 57 | 59 | self._client = app.get_client(contact.account)
|
| 60 | + self._client.connect_signal(
|
|
| 61 | + 'state-changed', self._on_client_state_changed)
|
|
| 62 | + |
|
| 58 | 63 | self._contact = contact
|
| 59 | 64 | self._contact.connect('avatar-update', self._on_avatar_update)
|
| 60 | 65 | |
| ... | ... | @@ -62,7 +67,7 @@ def __init__(self, |
| 62 | 67 | self._ui.connect_signals(self)
|
| 63 | 68 | |
| 64 | 69 | self._switcher = SideBarSwitcher(width=250)
|
| 65 | - self._switcher.set_stack(self._ui.main_stack)
|
|
| 70 | + self._switcher.set_stack(self._ui.main_stack, rows_visible=False)
|
|
| 66 | 71 | self._ui.main_grid.attach(self._switcher, 0, 0, 1, 1)
|
| 67 | 72 | self._ui.main_stack.connect('notify::visible-child-name',
|
| 68 | 73 | self._on_stack_child_changed)
|
| ... | ... | @@ -73,10 +78,14 @@ def __init__(self, |
| 73 | 78 | self._add_groupchat_info()
|
| 74 | 79 | self._add_groupchat_settings()
|
| 75 | 80 | self._add_groupchat_encryption()
|
| 76 | - self._add_groupchat_manage()
|
|
| 77 | - self._add_affiliations()
|
|
| 78 | - self._add_outcasts()
|
|
| 79 | - self._add_configuration()
|
|
| 81 | + |
|
| 82 | + if self._client.state.is_available:
|
|
| 83 | + self._ui.edit_name_button.set_sensitive(True)
|
|
| 84 | + self._ui.edit_name_button.set_tooltip_text(_('Edit Name…'))
|
|
| 85 | + self._add_groupchat_manage()
|
|
| 86 | + self._add_affiliations()
|
|
| 87 | + self._add_outcasts()
|
|
| 88 | + self._add_configuration()
|
|
| 80 | 89 | |
| 81 | 90 | self._load_avatar()
|
| 82 | 91 | self._ui.name_entry.set_text(contact.name)
|
| ... | ... | @@ -92,6 +101,16 @@ def __init__(self, |
| 92 | 101 | |
| 93 | 102 | self.show_all()
|
| 94 | 103 | |
| 104 | + def _on_client_state_changed(self,
|
|
| 105 | + _client: types.Client,
|
|
| 106 | + _signal_name: str,
|
|
| 107 | + state: SimpleClientState
|
|
| 108 | + ) -> None:
|
|
| 109 | + |
|
| 110 | + self._ui.edit_name_button.set_sensitive(state.is_connected)
|
|
| 111 | + self._ui.edit_name_button.set_tooltip_text(
|
|
| 112 | + _('Not connected') if not state.is_connected else _('Edit Name…'))
|
|
| 113 | + |
|
| 95 | 114 | def _on_muc_disco_update(self, event: MucDiscoUpdate) -> None:
|
| 96 | 115 | if event.jid != self._contact.jid:
|
| 97 | 116 | return
|
| ... | ... | @@ -138,6 +157,7 @@ def _add_groupchat_manage(self) -> None: |
| 138 | 157 | self._groupchat_manage = GroupchatManage(self.account,
|
| 139 | 158 | self._contact)
|
| 140 | 159 | self._ui.manage_box.add(self._groupchat_manage)
|
| 160 | + self._switcher.set_row_visible('manage', True)
|
|
| 141 | 161 | |
| 142 | 162 | def _add_groupchat_info(self) -> None:
|
| 143 | 163 | self._groupchat_info = GroupChatInfoScrolled(
|
| ... | ... | @@ -148,6 +168,7 @@ def _add_groupchat_info(self) -> None: |
| 148 | 168 | self._groupchat_info.set_from_disco_info(disco_info)
|
| 149 | 169 | self._groupchat_info.set_subject(self._contact.subject)
|
| 150 | 170 | self._ui.info_box.add(self._groupchat_info)
|
| 171 | + self._switcher.set_row_visible('information', True)
|
|
| 151 | 172 | |
| 152 | 173 | def _add_groupchat_settings(self) -> None:
|
| 153 | 174 | main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=24)
|
| ... | ... | @@ -174,6 +195,7 @@ def _add_groupchat_settings(self) -> None: |
| 174 | 195 | scrolled_window.add(main_box)
|
| 175 | 196 | |
| 176 | 197 | self._ui.settings_box.add(scrolled_window)
|
| 198 | + self._switcher.set_row_visible('settings', True)
|
|
| 177 | 199 | |
| 178 | 200 | def _add_groupchat_encryption(self) -> None:
|
| 179 | 201 | if (self._contact.is_groupchat and
|
| ... | ... | @@ -184,18 +206,22 @@ def _add_groupchat_encryption(self) -> None: |
| 184 | 206 | |
| 185 | 207 | self._ui.encryption_box.add(
|
| 186 | 208 | OMEMOTrustManager(self._contact.account, self._contact))
|
| 209 | + self._switcher.set_row_visible('encryption-omemo', True)
|
|
| 187 | 210 | |
| 188 | 211 | def _add_affiliations(self) -> None:
|
| 189 | 212 | affiliations = GroupchatAffiliation(self._client, self._contact)
|
| 190 | 213 | self._ui.affiliation_box.add(affiliations)
|
| 214 | + self._switcher.set_row_visible('affiliations', True)
|
|
| 191 | 215 | |
| 192 | 216 | def _add_outcasts(self) -> None:
|
| 193 | 217 | affiliations = GroupchatOutcasts(self._client, self._contact)
|
| 194 | 218 | self._ui.outcasts_box.add(affiliations)
|
| 219 | + self._switcher.set_row_visible('outcasts', True)
|
|
| 195 | 220 | |
| 196 | 221 | def _add_configuration(self) -> None:
|
| 197 | 222 | config = GroupchatConfig(self._client, self._contact)
|
| 198 | 223 | self._ui.configuration_box.add(config)
|
| 224 | + self._switcher.set_row_visible('config', True)
|
|
| 199 | 225 | |
| 200 | 226 | def _on_key_press(self,
|
| 201 | 227 | _widget: GroupchatDetails,
|