Daniel Brötzmann pushed to branch master at gajim / gajim

Commits:

6 changed files:

Changes:

  • gajim/common/storage/archive.py
    ... ... @@ -121,6 +121,8 @@ class LastConversationRow(NamedTuple):
    121 121
     
    
    122 122
     
    
    123 123
     class SearchLogRow(NamedTuple):
    
    124
    +    account_id: int
    
    125
    +    jid_id: int
    
    124 126
         contact_name: str
    
    125 127
         time: float
    
    126 128
         kind: int
    
    ... ... @@ -675,8 +677,8 @@ def search_log(self,
    675 677
                 users_query_string = 'AND UPPER(contact_name) IN (?)'
    
    676 678
     
    
    677 679
             sql = '''
    
    678
    -            SELECT contact_name, time, kind, show, message, subject,
    
    679
    -                   additional_data, log_line_id
    
    680
    +            SELECT account_id, jid_id, contact_name, time, kind, show, message,
    
    681
    +                   subject, additional_data, log_line_id
    
    680 682
                 FROM logs NATURAL JOIN jids WHERE jid IN ({jids})
    
    681 683
                 AND message LIKE like(?)
    
    682 684
                 AND kind NOT IN ({kinds})
    

  • gajim/gtk/features.py
    ... ... @@ -20,7 +20,7 @@
    20 20
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    21 21
     
    
    22 22
     from typing import NamedTuple
    
    23
    -from typing import List
    
    23
    +from typing import Optional
    
    24 24
     
    
    25 25
     import os
    
    26 26
     import sys
    
    ... ... @@ -38,7 +38,7 @@ class Feature(NamedTuple):
    38 38
         tooltip: str
    
    39 39
         dependency_u: str
    
    40 40
         dependency_w: str
    
    41
    -    enabled: bool
    
    41
    +    enabled: Optional[bool]
    
    42 42
     
    
    43 43
     
    
    44 44
     class Features(Gtk.ApplicationWindow):
    
    ... ... @@ -77,7 +77,7 @@ def __init__(self) -> None:
    77 77
     
    
    78 78
         def _on_key_press(self,
    
    79 79
                           _widget: Gtk.Widget,
    
    80
    -                      event: Gdk.EventButton) -> None:
    
    80
    +                      event: Gdk.EventKey) -> None:
    
    81 81
             if event.keyval == Gdk.KEY_Escape:
    
    82 82
                 self.destroy()
    
    83 83
     
    
    ... ... @@ -85,7 +85,7 @@ def _add_feature(self, feature: Feature) -> None:
    85 85
             item = FeatureItem(feature)
    
    86 86
             self.feature_listbox.add(item)
    
    87 87
     
    
    88
    -    def _get_features(self) -> List[Feature]:
    
    88
    +    def _get_features(self) -> list[Feature]:
    
    89 89
             notification_sounds_available: bool = (
    
    90 90
                 app.is_installed('GSOUND') or sys.platform in ('win32', 'darwin'))
    
    91 91
             notification_sounds_enabled: bool = app.settings.get('sounds_on')
    
    ... ... @@ -210,7 +210,7 @@ def __init__(self, feature: Feature) -> None:
    210 210
             self.add(self._icon)
    
    211 211
             self.add(self._box)
    
    212 212
     
    
    213
    -    def _set_feature(self, available: bool, enabled: bool) -> None:
    
    213
    +    def _set_feature(self, available: bool, enabled: Optional[bool]) -> None:
    
    214 214
             self._icon.get_style_context().remove_class('error-color')
    
    215 215
             self._icon.get_style_context().remove_class('warning-color')
    
    216 216
             self._icon.get_style_context().remove_class('success-color')
    
    ... ... @@ -219,7 +219,9 @@ def _set_feature(self, available: bool, enabled: bool) -> None:
    219 219
                 self._icon.set_from_icon_name(
    
    220 220
                     'window-close-symbolic', Gtk.IconSize.MENU)
    
    221 221
                 self._icon.get_style_context().add_class('error-color')
    
    222
    -        elif enabled is False:
    
    222
    +            return
    
    223
    +
    
    224
    +        if enabled is not None and not enabled:
    
    223 225
                 self._icon.set_from_icon_name(
    
    224 226
                     'dialog-warning-symbolic', Gtk.IconSize.MENU)
    
    225 227
                 self._box.pack_start(self._label_disabled, True, True, 0)
    

  • gajim/gtk/file_transfer_send.py
    ... ... @@ -17,16 +17,23 @@
    17 17
     # You should have received a copy of the GNU General Public License
    
    18 18
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    19 19
     
    
    20
    +from __future__ import annotations
    
    21
    +
    
    22
    +from typing import Callable
    
    23
    +from typing import cast
    
    24
    +
    
    20 25
     from pathlib import Path
    
    21 26
     
    
    22 27
     from gi.repository import Gdk
    
    23 28
     from gi.repository import Gtk
    
    24 29
     from gi.repository import Pango
    
    25 30
     
    
    26
    -from nbxmpp import Namespace
    
    31
    +from nbxmpp.namespaces import Namespace
    
    32
    +from nbxmpp.protocol import JID
    
    27 33
     
    
    28 34
     from gajim.common import app
    
    29 35
     from gajim.common.i18n import _
    
    36
    +from gajim.common.modules.contacts import BareContact
    
    30 37
     
    
    31 38
     from .filechoosers import FileChooserDialog
    
    32 39
     from .resource_selector import ResourceSelector
    
    ... ... @@ -34,7 +41,11 @@
    34 41
     
    
    35 42
     
    
    36 43
     class SendFileDialog(Gtk.ApplicationWindow):
    
    37
    -    def __init__(self, contact, send_callback, transient_for):
    
    44
    +    def __init__(self,
    
    45
    +                 contact: BareContact,
    
    46
    +                 send_callback: Callable[..., bool],
    
    47
    +                 transient_for: Gtk.Window
    
    48
    +                 ) -> None:
    
    38 49
             Gtk.ApplicationWindow.__init__(self)
    
    39 50
             self.set_application(app.app)
    
    40 51
             self.set_show_menubar(False)
    
    ... ... @@ -61,24 +72,28 @@ def __init__(self, contact, send_callback, transient_for):
    61 72
                 constraints=[Namespace.JINGLE_FILE_TRANSFER_5])
    
    62 73
             self._resource_selector.connect(
    
    63 74
                 'selection-changed', self._on_resource_selection)
    
    64
    -        self._ui.resource_box.pack_start(self._resource_selector, 1, 0, 0)
    
    75
    +        self._ui.resource_box.pack_start(
    
    76
    +            self._resource_selector, True, False, 0)
    
    65 77
     
    
    66
    -        self.connect('key-press-event', self._key_press_event)
    
    78
    +        self.connect('key-press-event', self._on_key_press)
    
    67 79
             self._ui.connect_signals(self)
    
    68 80
             self.show_all()
    
    69 81
     
    
    70
    -    def _on_files_changed(self, _listbox, _row):
    
    82
    +    def _on_files_changed(self, _listbox: Gtk.ListBox, _row: FileRow) -> None:
    
    71 83
             sensitive = bool(len(self._ui.listbox.get_children()) > 0)
    
    72 84
             self._ui.files_send.set_sensitive(sensitive)
    
    73 85
     
    
    74
    -    def _on_resource_selection(self, _selector, state):
    
    86
    +    def _on_resource_selection(self,
    
    87
    +                               _selector: ResourceSelector,
    
    88
    +                               state: bool
    
    89
    +                               ) -> None:
    
    75 90
             self._ui.resource_send.set_sensitive(state)
    
    76 91
     
    
    77
    -    def _on_send_to_resource(self, _button):
    
    92
    +    def _on_send_to_resource(self, _button: Gtk.Button) -> None:
    
    78 93
             resource_jid = self._resource_selector.get_jid()
    
    79 94
             self._send_files(resource_jid)
    
    80 95
     
    
    81
    -    def _on_send_clicked(self, _button):
    
    96
    +    def _on_send_clicked(self, _button: Gtk.Button) -> None:
    
    82 97
             count = len(self._contact.get_resources())
    
    83 98
             if count == 0 or count > 1:
    
    84 99
                 self._ui.send_stack.set_visible_child_name('resource_selection')
    
    ... ... @@ -87,48 +102,50 @@ def _on_send_clicked(self, _button):
    87 102
             resource_jid = self._contact.get_resources()[0].jid
    
    88 103
             self._send_files(resource_jid)
    
    89 104
     
    
    90
    -    def _send_files(self, resource_jid):
    
    91
    -        for file in self._ui.listbox.get_children():
    
    105
    +    def _send_files(self, resource_jid: JID) -> None:
    
    106
    +        files = cast(list[FileRow], self._ui.listbox.get_children())
    
    107
    +        for file in files:
    
    92 108
                 self._send_callback(
    
    93
    -                resource_jid, str(file.path), self._get_description())
    
    109
    +                resource_jid, str(file.file_path), self._get_description())
    
    94 110
             self.destroy()
    
    95 111
     
    
    96
    -    def _select_files(self, _button):
    
    112
    +    def _select_files(self, _button: Gtk.Button) -> None:
    
    97 113
             FileChooserDialog(self._set_files,
    
    98 114
                               select_multiple=True,
    
    99 115
                               transient_for=self,
    
    100 116
                               path=app.settings.get('last_send_dir'))
    
    101 117
     
    
    102
    -    def _remove_files(self, _button):
    
    118
    +    def _remove_files(self, _button: Gtk.Button) -> None:
    
    103 119
             selected = self._ui.listbox.get_selected_rows()
    
    104 120
             for item in selected:
    
    105 121
                 self._ui.listbox.remove(item)
    
    106 122
     
    
    107
    -    def _set_files(self, file_names):
    
    123
    +    def _set_files(self, file_names: str) -> None:
    
    124
    +        last_dir = ''
    
    108 125
             for file in file_names:
    
    109 126
                 row = FileRow(file)
    
    110
    -            if row.path.is_dir():
    
    127
    +            if row.file_path.is_dir():
    
    111 128
                     continue
    
    112
    -            last_dir = row.path.parent
    
    129
    +            last_dir = row.file_path.parent
    
    113 130
                 self._ui.listbox.add(row)
    
    114 131
             self._ui.listbox.show_all()
    
    115 132
             app.settings.set('last_send_dir', str(last_dir))
    
    116 133
     
    
    117
    -    def _get_description(self):
    
    134
    +    def _get_description(self) -> str:
    
    118 135
             buffer_ = self._ui.description.get_buffer()
    
    119 136
             start, end = buffer_.get_bounds()
    
    120 137
             return buffer_.get_text(start, end, False)
    
    121 138
     
    
    122
    -    def _key_press_event(self, _widget, event):
    
    139
    +    def _on_key_press(self, _widget: Gtk.Widget, event: Gdk.EventKey) -> None:
    
    123 140
             if event.keyval == Gdk.KEY_Escape:
    
    124 141
                 self.destroy()
    
    125 142
     
    
    126 143
     
    
    127 144
     class FileRow(Gtk.ListBoxRow):
    
    128
    -    def __init__(self, path):
    
    145
    +    def __init__(self, path: str) -> None:
    
    129 146
             Gtk.ListBoxRow.__init__(self)
    
    130
    -        self.path = Path(path)
    
    131
    -        label = Gtk.Label(label=self.path.name)
    
    147
    +        self.file_path: Path = Path(path)
    
    148
    +        label = Gtk.Label(label=self.file_path.name)
    
    132 149
             label.set_ellipsize(Pango.EllipsizeMode.END)
    
    133 150
             label.set_xalign(0)
    
    134 151
             self.add(label)

  • gajim/gtk/preview_audio.py
    ... ... @@ -79,6 +79,7 @@ def _build_audio_widget(self) -> None:
    79 79
             self.show_all()
    
    80 80
     
    
    81 81
         def _setup_audio_player(self, file_path: Path) -> None:
    
    82
    +        assert self._playbin is not None
    
    82 83
             self._playbin.set_property('uri', f'file://{file_path}')
    
    83 84
             state_return = self._playbin.set_state(Gst.State.PAUSED)
    
    84 85
             if state_return == Gst.StateChangeReturn.FAILURE:
    
    ... ... @@ -93,13 +94,17 @@ def _setup_audio_player(self, file_path: Path) -> None:
    93 94
             bus.connect('message', self._on_bus_message)
    
    94 95
     
    
    95 96
         def _on_bus_message(self, _bus: Gst.Bus, message: Gst.Message) -> None:
    
    97
    +        assert self._playbin is not None
    
    96 98
             if message.type == Gst.MessageType.EOS:
    
    97 99
                 self._set_pause(True)
    
    98 100
                 self._playbin.seek_simple(
    
    99 101
                     Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0)
    
    100 102
             elif message.type == Gst.MessageType.STATE_CHANGED:
    
    101
    -            _success, duration = self._playbin.query_duration(
    
    103
    +            success, duration = self._playbin.query_duration(
    
    102 104
                     Gst.Format.TIME)
    
    105
    +            if not success:
    
    106
    +                return
    
    107
    +            assert duration is not None
    
    103 108
                 if duration > 0:
    
    104 109
                     self._seek_bar.set_range(0.0, duration)
    
    105 110
     
    
    ... ... @@ -114,6 +119,7 @@ def _on_seek(self,
    114 119
                      _scroll: Gtk.ScrollType,
    
    115 120
                      value: float
    
    116 121
                      ) -> bool:
    
    122
    +        assert self._playbin is not None
    
    117 123
             self._playbin.seek_simple(
    
    118 124
                 Gst.Format.TIME, Gst.SeekFlags.FLUSH, int(value))
    
    119 125
             return False
    
    ... ... @@ -122,13 +128,16 @@ def _on_play_clicked(self, _button: Gtk.Button) -> None:
    122 128
             self._set_pause(not self._get_paused())
    
    123 129
     
    
    124 130
         def _on_destroy(self, _widget: Gtk.Widget) -> None:
    
    131
    +        assert self._playbin is not None
    
    125 132
             self._playbin.set_state(Gst.State.NULL)
    
    126 133
     
    
    127 134
         def _get_paused(self) -> bool:
    
    135
    +        assert self._playbin is not None
    
    128 136
             _, state, _ = self._playbin.get_state(20)
    
    129 137
             return state == Gst.State.PAUSED
    
    130 138
     
    
    131 139
         def _set_pause(self, paused: bool) -> None:
    
    140
    +        assert self._playbin is not None
    
    132 141
             if paused:
    
    133 142
                 self._playbin.set_state(Gst.State.PAUSED)
    
    134 143
                 self._play_icon.set_from_icon_name(
    
    ... ... @@ -145,6 +154,7 @@ def _update_seek_bar(self) -> bool:
    145 154
                 self._has_timeout = False
    
    146 155
                 return False
    
    147 156
     
    
    157
    +        assert self._playbin is not None
    
    148 158
             if self._playbin.query(self._query):
    
    149 159
                 _fmt, cur_pos = self._query.parse_position()
    
    150 160
                 if cur_pos is not None:
    
    ... ... @@ -167,4 +177,6 @@ def _format_audio_timestamp(_widget: Gtk.Scale, ns: float) -> str:
    167 177
     
    
    168 178
         @staticmethod
    
    169 179
         def _on_realize(event_box: Gtk.EventBox) -> None:
    
    170
    -        event_box.get_window().set_cursor(get_cursor('pointer'))
    180
    +        window = event_box.get_window()
    
    181
    +        if window is not None:
    
    182
    +            window.set_cursor(get_cursor('pointer'))

  • gajim/gtk/resource_selector.py
    ... ... @@ -14,7 +14,7 @@
    14 14
     
    
    15 15
     from __future__ import annotations
    
    16 16
     from typing import Optional
    
    17
    -from typing import Tuple
    
    17
    +from typing import cast
    
    18 18
     
    
    19 19
     import locale
    
    20 20
     import logging
    
    ... ... @@ -23,7 +23,6 @@
    23 23
     from gi.repository import Gtk
    
    24 24
     
    
    25 25
     from nbxmpp import JID
    
    26
    -from nbxmpp.namespaces import _Namespaces
    
    27 26
     from nbxmpp.structs import DiscoInfo
    
    28 27
     
    
    29 28
     from gajim.common import app
    
    ... ... @@ -46,7 +45,7 @@ class ResourceSelector(Gtk.ScrolledWindow):
    46 45
     
    
    47 46
         def __init__(self,
    
    48 47
                      contact: BareContact,
    
    49
    -                 constraints: Optional[list[_Namespaces]] = None) -> None:
    
    48
    +                 constraints: Optional[list[str]] = None) -> None:
    
    50 49
             Gtk.ScrolledWindow.__init__(self)
    
    51 50
             self.set_shadow_type(Gtk.ShadowType.IN)
    
    52 51
             self.set_size_request(-1, 200)
    
    ... ... @@ -61,7 +60,7 @@ def __init__(self,
    61 60
             self._contact.connect('presence-update', self._on_update)
    
    62 61
             self._contact.connect('caps-update', self._on_update)
    
    63 62
     
    
    64
    -        # Constraints include Namespaces a resource has to support
    
    63
    +        # Constraints include nbxmpp Namespaces a resource has to support
    
    65 64
             self._constraints = constraints or []
    
    66 65
     
    
    67 66
             self._set_placeholder()
    
    ... ... @@ -106,13 +105,14 @@ def _on_update(self,
    106 105
             self._add_entries()
    
    107 106
     
    
    108 107
         def get_jid(self) -> JID:
    
    109
    -        return self._listbox.get_selected_row().jid
    
    108
    +        resource_row = cast(ResourceRow, self._listbox.get_selected_row())
    
    109
    +        return resource_row.jid
    
    110 110
     
    
    111 111
     
    
    112 112
     class ResourceRow(Gtk.ListBoxRow):
    
    113 113
         def __init__(self,
    
    114 114
                      resource_contact: ResourceContact,
    
    115
    -                 constraints: list[_Namespaces]
    
    115
    +                 constraints: list[str]
    
    116 116
                      ) -> None:
    
    117 117
             Gtk.ListBoxRow.__init__(self)
    
    118 118
     
    
    ... ... @@ -145,7 +145,7 @@ def __init__(self,
    145 145
             box.add(name_label)
    
    146 146
     
    
    147 147
             for constraint in constraints:
    
    148
    -            if not resource_contact.supports(str(constraint)):
    
    148
    +            if not resource_contact.supports(constraint):
    
    149 149
                     self.set_sensitive(False)
    
    150 150
                     self.set_tooltip_text(_('This devices is not compatible.'))
    
    151 151
     
    
    ... ... @@ -154,7 +154,7 @@ def __init__(self,
    154 154
     
    
    155 155
         @staticmethod
    
    156 156
         def _get_client_identity(disco_info: DiscoInfo
    
    157
    -                             ) -> Tuple[Optional[str], Optional[str]]:
    
    157
    +                             ) -> tuple[Optional[str], Optional[str]]:
    
    158 158
             for identity in disco_info.identities:
    
    159 159
                 if identity.category == 'client':
    
    160 160
                     return identity.name, identity.type
    

  • gajim/gtk/search_view.py
    ... ... @@ -35,6 +35,7 @@
    35 35
     from gajim.common.const import KindConstant
    
    36 36
     from gajim.common.const import FILE_CATEGORIES
    
    37 37
     from gajim.common.i18n import _
    
    38
    +from gajim.common.storage.archive import SearchLogRow
    
    38 39
     
    
    39 40
     from .conversation.message_widget import MessageWidget
    
    40 41
     from .builder import get_builder
    
    ... ... @@ -56,8 +57,8 @@ def __init__(self) -> None:
    56 57
     
    
    57 58
             self._account: Optional[str] = None
    
    58 59
             self._jid: Optional[JID] = None
    
    59
    -        self._results: list[str] = []
    
    60
    -        self._scope: Optional[str] = None
    
    60
    +        self._results: list[SearchLogRow] = []
    
    61
    +        self._scope: str = 'everywhere'
    
    61 62
     
    
    62 63
             self._ui = get_builder('search_view.ui')
    
    63 64
             self._ui.results_listbox.set_header_func(self._header_func)
    
    ... ... @@ -86,6 +87,9 @@ def _header_func(row: Union[CounterRow, ResultRow],
    86 87
                 else:
    
    87 88
                     row.set_header(RowHeader(row.account, row.jid, row.time))
    
    88 89
             else:
    
    90
    +            if isinstance(row, CounterRow):
    
    91
    +                row.set_header(None)
    
    92
    +                return
    
    89 93
                 date1 = time.strftime('%x', time.localtime(row.time))
    
    90 94
                 date2 = time.strftime('%x', time.localtime(before.time))
    
    91 95
                 if before.jid != row.jid:
    
    ... ... @@ -184,13 +188,13 @@ def _on_search(self, entry: Gtk.Entry) -> None:
    184 188
     
    
    185 189
         def _filter_results_for_files(self, filetypes: list[str]) -> None:
    
    186 190
             if 'file' in filetypes:
    
    187
    -            results = []
    
    191
    +            results: list[SearchLogRow] = []
    
    188 192
                 for result in self._results:
    
    189 193
                     if result.additional_data.get_value('gajim', 'oob_url'):
    
    190 194
                         results.append(result)
    
    191 195
                 self._results = results
    
    192 196
             else:
    
    193
    -            results = []
    
    197
    +            results: list[SearchLogRow] = []
    
    194 198
                 for result in self._results:
    
    195 199
                     url = result.additional_data.get_value('gajim', 'oob_url')
    
    196 200
                     if url is None:
    
    ... ... @@ -223,11 +227,14 @@ def _add_results(self) -> None:
    223 227
             accounts = self._get_accounts()
    
    224 228
             for msg in self._results[:25]:
    
    225 229
                 if self._scope == 'everywhere':
    
    230
    +                archive_jid = app.storage.archive.get_jid_from_id(msg.jid_id)
    
    226 231
                     result_row = ResultRow(
    
    227 232
                         msg,
    
    228
    -                    accounts.get(msg.account_id),
    
    229
    -                    app.storage.archive.get_jid_from_id(msg.jid_id).jid)
    
    233
    +                    accounts[msg.account_id],
    
    234
    +                    archive_jid.jid)
    
    230 235
                 else:
    
    236
    +                assert self._account is not None
    
    237
    +                assert self._jid is not None
    
    231 238
                     result_row = ResultRow(msg, self._account, self._jid)
    
    232 239
     
    
    233 240
                 self._ui.results_listbox.add(result_row)
    
    ... ... @@ -242,8 +249,8 @@ def _on_edge_reached(self,
    242 249
             self._add_results()
    
    243 250
     
    
    244 251
         @staticmethod
    
    245
    -    def _get_accounts() -> dict[str, str]:
    
    246
    -        accounts: dict[str, str] = {}
    
    252
    +    def _get_accounts() -> dict[int, str]:
    
    253
    +        accounts: dict[int, str] = {}
    
    247 254
             for account in app.settings.get_accounts():
    
    248 255
                 account_id = app.storage.archive.get_account_id(account)
    
    249 256
                 accounts[account_id] = account