Daniel Brötzmann pushed to branch master at gajim / gajim
Commits: b86e41a5 by wurstsalat at 2022-05-21T21:31:36+02:00 imprv: Add log_line_id to CallRow and FileTransferJingleRow
This enables jumping to these rows by log_line_id
- - - - - d5fe10e3 by wurstsalat at 2022-05-21T21:31:36+02:00 feat: SearchView: Add calendar for browsing history
- - - - -
6 changed files:
- gajim/common/storage/archive.py - gajim/data/gui/search_view.ui - gajim/gtk/builder.pyi - gajim/gtk/conversation/rows/call.py - gajim/gtk/conversation/rows/file_transfer_jingle.py - gajim/gtk/search_view.py
Changes:
===================================== gajim/common/storage/archive.py ===================================== @@ -134,6 +134,15 @@ class SearchLogRow(NamedTuple): log_line_id: int
+class HistoryDayRow(NamedTuple): + day: int + + +class MessageMetaRow(NamedTuple): + log_line_id: int + time: float + + class LastArchiveMessageRow(NamedTuple): id: int last_mam_id: str @@ -551,46 +560,6 @@ def get_conversation_between(self, sql, tuple(jids) + (before, after)).fetchall()
- @timeit - def get_messages_for_date(self, - account: str, - jid: str, - date: datetime.datetime): - """ - Load the complete conversation with a given jid on a specific date - - :param account: The account - - :param jid: The jid for which we request the conversation - - :param date: datetime.datetime instance - example: datetime.datetime(year, month, day) - - returns a list of namedtuples - """ - - jids = [jid] - account_id = self.get_account_id(account) - - delta = datetime.timedelta( - hours=23, minutes=59, seconds=59, microseconds=999999) - date_ts = date.timestamp() - delta_ts = (date + delta).timestamp() - - sql = ''' - SELECT contact_name, time, kind, show, message, subject, - additional_data, log_line_id - FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) - AND account_id = {account_id} - AND time BETWEEN ? AND ? - ORDER BY time DESC, log_line_id DESC - '''.format(jids=', '.join('?' * len(jids)), - account_id=account_id) - - return self._con.execute( - sql, - tuple(jids) + (date_ts, delta_ts)).fetchall() - @timeit def search_log(self, _account: str, @@ -751,27 +720,17 @@ def search_all_logs(self, yield result
@timeit - def get_days_with_logs(self, - _account: str, - jid: str, - year: int, - month: int) -> Any: + def get_days_with_history(self, + account: str, + jid: str, + year: int, + month: int + ) -> list[HistoryDayRow]: """ - Request the days in a month where we received messages - for a given `jid`. - - :param account: The account - - :param jid: The jid for which we request the days - - :param year: The year - - :param month: The month - - returns a list of namedtuples + Get days in month where messages for 'jid' exist """ jids = [jid] - + account_id = self.get_account_id(account) kinds = map(str, [KindConstant.STATUS, KindConstant.GCSTATUS])
@@ -785,10 +744,12 @@ def get_days_with_logs(self, SELECT DISTINCT CAST(strftime('%d', time, 'unixepoch', 'localtime') AS INTEGER) AS day FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) + AND account_id = {account_id} AND time BETWEEN ? AND ? AND kind NOT IN ({kinds}) ORDER BY time """.format(jids=', '.join('?' * len(jids)), + account_id=account_id, kinds=', '.join(kinds))
return self._con.execute(sql, tuple(jids) + @@ -796,26 +757,25 @@ def get_days_with_logs(self, (date + delta).timestamp())).fetchall()
@timeit - def get_last_date_that_has_logs(self, _account: str, jid: str) -> Any: + def get_last_history_timestamp(self, + account: str, + jid: str + ) -> Optional[float]: """ - Get the timestamp of the last message we received for the jid. - - :param account: The account - - :param jid: The jid for which we request the last timestamp - - returns a timestamp or None + Get the timestamp of the last message we received for the jid """ jids = [jid] - + account_id = self.get_account_id(account) kinds = map(str, [KindConstant.STATUS, KindConstant.GCSTATUS])
sql = ''' SELECT MAX(time) as time FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) + AND account_id = {account_id} AND kind NOT IN ({kinds}) - '''.format(jids=', '.join('?' * len(jids)), + '''.format(account_id=account_id, + jids=', '.join('?' * len(jids)), kinds=', '.join(kinds))
# fetchone() returns always at least one Row with all @@ -823,26 +783,25 @@ def get_last_date_that_has_logs(self, _account: str, jid: str) -> Any: return self._con.execute(sql, tuple(jids)).fetchone().time
@timeit - def get_first_date_that_has_logs(self, _account: str, jid: str) -> Any: + def get_first_history_timestamp(self, + account: str, + jid: str + ) -> Optional[float]: """ - Get the timestamp of the first message we received for the jid. - - :param account: The account - - :param jid: The jid for which we request the first timestamp - - returns a timestamp or None + Get the timestamp of the first message we received for the jid """ jids = [jid] - + account_id = self.get_account_id(account) kinds = map(str, [KindConstant.STATUS, KindConstant.GCSTATUS])
sql = ''' SELECT MIN(time) as time FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) + AND account_id = {account_id} AND kind NOT IN ({kinds}) '''.format(jids=', '.join('?' * len(jids)), + account_id=account_id, kinds=', '.join(kinds))
# fetchone() returns always at least one Row with all @@ -850,25 +809,17 @@ def get_first_date_that_has_logs(self, _account: str, jid: str) -> Any: return self._con.execute(sql, tuple(jids)).fetchone().time
@timeit - def get_date_has_logs(self, - _account: str, - jid: str, - date: datetime.datetime) -> Any: + def date_has_history(self, + account: str, + jid: str, + date: datetime.datetime + ) -> Optional[float]: """ - Get single timestamp of a message we received for the jid - in the time range of one day. - - :param account: The account - - :param jid: The jid for which we request the first timestamp - - :param date: datetime.datetime instance - example: datetime.datetime(year, month, day) - - returns a timestamp or None + Get a single timestamp of a message for 'jid' + in time range of one day """ jids = [jid] - + account_id = self.get_account_id(account) delta = datetime.timedelta( hours=23, minutes=59, seconds=59, microseconds=999999)
@@ -878,12 +829,44 @@ def get_date_has_logs(self, sql = ''' SELECT time FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) + AND account_id = {account_id} AND time BETWEEN ? AND ? - '''.format(jids=', '.join('?' * len(jids))) + '''.format(jids=', '.join('?' * len(jids)), + account_id=account_id)
return self._con.execute( sql, tuple(jids) + (start, end)).fetchone()
+ @timeit + def get_first_message_meta_for_date(self, + account: str, + jid: str, + date: datetime.datetime + ) -> Optional[MessageMetaRow]: + """ + Load meta data for the first message of a specific date + """ + jids = [jid] + account_id = self.get_account_id(account) + + delta = datetime.timedelta( + hours=23, minutes=59, seconds=59, microseconds=999999) + date_ts = date.timestamp() + delta_ts = (date + delta).timestamp() + + sql = ''' + SELECT time, log_line_id + FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) + AND account_id = {account_id} + AND time BETWEEN ? AND ? + ORDER BY time ASC, log_line_id ASC + '''.format(jids=', '.join('?' * len(jids)), + account_id=account_id) + + return self._con.execute( + sql, + tuple(jids) + (date_ts, delta_ts)).fetchone() + @timeit def deduplicate_muc_message(self, account: str,
===================================== gajim/data/gui/search_view.ui ===================================== @@ -2,121 +2,130 @@ <!-- Generated with glade 3.38.2 --> <interface> <requires lib="gtk+" version="3.24"/> - <object class="GtkBox" id="header_box"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="hexpand">True</property> - <property name="spacing">6</property> - <child> - <object class="GtkSeparator"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="valign">center</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="header_name_label"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="ellipsize">end</property> - <property name="max-width-chars">15</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="header_date_label"> - <property name="visible">True</property> - <property name="can-focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkSeparator"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="valign">center</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - <style> - <class name="search-view-header"/> - </style> - </object> - <!-- n-columns=3 n-rows=2 --> - <object class="GtkGrid" id="result_row_grid"> - <property name="visible">True</property> + <object class="GtkPopover" id="calendar_popover"> <property name="can-focus">False</property> - <property name="row-spacing">3</property> - <property name="column-spacing">12</property> - <child> - <object class="GtkImage" id="row_avatar"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="valign">start</property> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> - <property name="height">2</property> - </packing> - </child> <child> - <object class="GtkLabel" id="row_time_label"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="halign">end</property> - <property name="single-line-mode">True</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left-attach">2</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="row_name_label"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="halign">start</property> - <property name="ellipsize">end</property> - <property name="single-line-mode">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCalendar" id="calendar"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="year">2022</property> + <property name="month">4</property> + <property name="day">21</property> + <signal name="day-selected" handler="_on_date_selected" swapped="no"/> + <signal name="month-changed" handler="_on_month_changed" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkButton"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_first_date_selected" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">go-first-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_previous_date_selected" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">go-previous-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_next_date_selected" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">go-next-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_last_date_selected" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">go-last-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <style> + <class name="linked"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> <style> - <class name="bold"/> + <class name="padding-6"/> </style> </object> - <packing> - <property name="left-attach">1</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> </child> - <style> - <class name="search-view-row-grid"/> - </style> </object> <object class="GtkBox" id="search_box"> <property name="visible">True</property> @@ -141,7 +150,8 @@ <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="pack-type">end</property> + <property name="position">3</property> </packing> </child> <child> @@ -163,7 +173,30 @@ <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">0</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkMenuButton" id="calendar_button"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="focus-on-click">False</property> + <property name="receives-default">True</property> + <property name="tooltip-text" translatable="yes">Browse history by date</property> + <property name="popover">calendar_popover</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">x-office-calendar-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack-type">end</property> + <property name="position">2</property> </packing> </child> </object> @@ -304,4 +337,120 @@ <class name="search-view"/> </style> </object> + <object class="GtkBox" id="header_box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkSeparator"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">center</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="header_name_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="ellipsize">end</property> + <property name="max-width-chars">15</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="header_date_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkSeparator"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">center</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <style> + <class name="search-view-header"/> + </style> + </object> + <!-- n-columns=3 n-rows=2 --> + <object class="GtkGrid" id="result_row_grid"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="row-spacing">3</property> + <property name="column-spacing">12</property> + <child> + <object class="GtkImage" id="row_avatar"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">start</property> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> + <property name="height">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="row_time_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">end</property> + <property name="single-line-mode">True</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left-attach">2</property> + <property name="top-attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="row_name_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">start</property> + <property name="ellipsize">end</property> + <property name="single-line-mode">True</property> + <style> + <class name="bold"/> + </style> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <style> + <class name="search-view-row-grid"/> + </style> + </object> </interface>
===================================== gajim/gtk/builder.pyi ===================================== @@ -803,6 +803,15 @@ class RosterTooltipBuilder(Builder):
class SearchViewBuilder(Builder): + calendar_popover: Gtk.Popover + calendar: Gtk.Calendar + search_box: Gtk.Box + calendar_button: Gtk.MenuButton + search_entry: Gtk.SearchEntry + search_checkbutton: Gtk.CheckButton + date_hint: Gtk.Label + results_listbox: Gtk.ListBox + placeholder: Gtk.Box header_box: Gtk.Box header_name_label: Gtk.Label header_date_label: Gtk.Label @@ -810,12 +819,6 @@ class SearchViewBuilder(Builder): row_avatar: Gtk.Image row_time_label: Gtk.Label row_name_label: Gtk.Label - search_box: Gtk.Box - search_entry: Gtk.SearchEntry - search_checkbutton: Gtk.CheckButton - date_hint: Gtk.Label - results_listbox: Gtk.ListBox - placeholder: Gtk.Box
class ServerInfoBuilder(Builder):
===================================== gajim/gtk/conversation/rows/call.py ===================================== @@ -80,6 +80,7 @@ def __init__(self, sid = db_message.additional_data.get_value('gajim', 'sid') module = self._client.get_module('Jingle') self._session = module.get_jingle_session(self._contact.jid, sid) + self.log_line_id = db_message.log_line_id
self._avatar_placeholder = Gtk.Box() self._avatar_placeholder.set_size_request(AvatarSize.ROSTER, -1)
===================================== gajim/gtk/conversation/rows/file_transfer_jingle.py ===================================== @@ -82,6 +82,7 @@ def __init__(self, self._file_props = FilesProp.getFilePropBySid(sid) if self._file_props is None: log.debug('File prop not found for SID: %s', sid) + self.log_line_id = db_message.log_line_id else: assert event is not None self._file_props = event.file_props
===================================== gajim/gtk/search_view.py ===================================== @@ -18,7 +18,8 @@ from typing import Iterator from typing import Optional
-import datetime +from datetime import datetime +from datetime import timedelta import itertools import logging import time @@ -33,11 +34,14 @@ from gajim.common import app from gajim.common import ged from gajim.common.const import AvatarSize +from gajim.common.const import Direction from gajim.common.const import KindConstant from gajim.common.storage.archive import SearchLogRow
from .conversation.message_widget import MessageWidget from .builder import get_builder +from .util import gtk_month +from .util import python_month
log = logging.getLogger('gajim.gui.search_view')
@@ -59,6 +63,9 @@ def __init__(self) -> None: self._results_iterator: Optional[Iterator[SearchLogRow]] = None self._scope: str = 'everywhere'
+ self._first_date: Optional[datetime] = None + self._last_date: Optional[datetime] = None + self._ui = get_builder('search_view.ui') self._ui.results_listbox.set_header_func(self._header_func) self.add(self._ui.search_box) @@ -71,6 +78,7 @@ def __init__(self) -> None: app.ged.register_event_handler('account-disabled', ged.GUI1, self._on_account_state) + self._update_calendar() self.show_all()
def _on_account_state(self, _event: Any) -> None: @@ -119,7 +127,7 @@ def _on_search(self, entry: Gtk.Entry) -> None: text, before_filters = self._strip_filters(text, 'before') if before_filters is not None: try: - before_filters = min([datetime.datetime.fromisoformat(date) for + before_filters = min([datetime.fromisoformat(date) for date in before_filters]) except ValueError: self._ui.date_hint.show() @@ -129,14 +137,14 @@ def _on_search(self, entry: Gtk.Entry) -> None: text, after_filters = self._strip_filters(text, 'after') if after_filters is not None: try: - after_filters = min([datetime.datetime.fromisoformat(date) for + after_filters = min([datetime.fromisoformat(date) for date in after_filters]) # if only the day is specified, we want to look after the # end of that day. # if precision is increased,we do want to look during the # day as well. if after_filters.hour == after_filters.minute == 0: - after_filters += datetime.timedelta(days=1) + after_filters += timedelta(days=1) except ValueError: self._ui.date_hint.show() return @@ -202,6 +210,120 @@ def _on_edge_reached(self,
self._add_results()
+ def _update_calendar(self) -> None: + if self._scope == 'everywhere': + self._ui.calendar_button.set_sensitive(False) + else: + self._ui.calendar_button.set_sensitive(True) + + first_log = app.storage.archive.get_first_history_timestamp( + self._account, self._jid) + if first_log is None: + return + self._first_date = self._get_date_from_timestamp(first_log) + last_log = app.storage.archive.get_last_history_timestamp( + self._account, self._jid) + if last_log is None: + return + self._last_date = self._get_date_from_timestamp(last_log) + + month = gtk_month(self._last_date.month) + self._ui.calendar.select_month(month, self._last_date.year) + + def _on_month_changed(self, calendar: Gtk.Calendar) -> None: + # Mark days with history in calendar + year, month, _day = calendar.get_date() + if year < 1900: + calendar.select_month(0, 1900) + calendar.select_day(1) + return + + calendar.clear_marks() + month = python_month(month) + + history_days = app.storage.archive.get_days_with_history( + self._account, self._jid, year, month) + for date in history_days: + calendar.mark_day(date.day) + + def _on_date_selected(self, calendar: Gtk.Calendar) -> None: + year, month, day = calendar.get_date() + py_m = python_month(month) + date = datetime(year, py_m, day) + self._scroll_to_date(date) + + def _on_first_date_selected(self, _button: Gtk.Button) -> None: + assert self._first_date is not None + gtk_m = gtk_month(self._first_date.month) + self._ui.calendar.select_month(gtk_m, self._first_date.year) + self._ui.calendar.select_day(self._first_date.day) + + def _on_last_date_selected(self, _button: Gtk.Button) -> None: + assert self._last_date is not None + gtk_m = gtk_month(self._last_date.month) + self._ui.calendar.select_month(gtk_m, self._last_date.year) + self._ui.calendar.select_day(self._last_date.day) + + def _on_previous_date_selected(self, _button: Gtk.Button) -> None: + delta = timedelta(days=-1) + assert self._first_date is not None + self._select_date(delta, self._first_date, Direction.NEXT) + + def _on_next_date_selected(self, _button: Gtk.Button) -> None: + delta = timedelta(days=1) + assert self._last_date is not None + self._select_date(delta, self._last_date, Direction.PREV) + + def _select_date(self, + delta: timedelta, + end_date: datetime, + direction: Direction + ) -> None: + # Iterate through days until history entry found or + # supplied end_date (first_date/last_date) reached + year, month, day = self._ui.calendar.get_date() + py_m = python_month(month) + date = datetime(year, py_m, day) + + history = None + while history is None: + if direction == Direction.PREV: + if end_date <= date: + return + else: + if end_date >= date: + return + + date = date + delta + if date == end_date: + break + history = app.storage.archive.date_has_history( + self._account, self._jid, date) + + gtk_m = gtk_month(date.month) + self._ui.calendar.select_month(gtk_m, date.year) + self._ui.calendar.select_day(date.day) + + def _scroll_to_date(self, date: datetime) -> None: + control = app.window.get_active_control() + if control is None: + return + if control.contact.jid == self._jid: + meta_row = app.storage.archive.get_first_message_meta_for_date( + self._account, self._jid, date) + if meta_row is None: + return + control.scroll_to_message( + meta_row.log_line_id, + meta_row.time) + + @staticmethod + def _get_date_from_timestamp(timestamp: float) -> datetime: + localtime = time.localtime(timestamp) + year, month, day = localtime[0], localtime[1], localtime[2] + date = datetime(year, month, day) + return date + @staticmethod def _get_accounts() -> dict[int, str]: accounts: dict[int, str] = {} @@ -232,7 +354,14 @@ def set_focus(self) -> None: def set_context(self, account: Optional[str], jid: Optional[JID]) -> None: self._account = account self._jid = jid - self._ui.search_checkbutton.set_active(jid is None) + everywhere = bool(jid is None) + self._ui.search_checkbutton.set_active(everywhere) + context = account is not None and jid is not None + if not context or everywhere: + self._scope = 'everywhere' + else: + self._scope = 'contact' + self._update_calendar()
class RowHeader(Gtk.Box):
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/22cfbb00c2c237b1c95502ae49c70cf7...