diff --git a/dzgui/api/servers.py b/dzgui/api/servers.py index e603c1a..c31ee5b 100644 --- a/dzgui/api/servers.py +++ b/dzgui/api/servers.py @@ -397,7 +397,11 @@ def query_api(key: str, appid: int, param: str) -> Res: "key": key, } try: - res = requests.get(STEAM_SERVERS, params=payload, timeout=5) + """ + NOTE: the global default timeout is None; + using a low timeout (~5s) sometimes fails + """ + res = requests.get(STEAM_SERVERS, params=payload, timeout=REQUEST_TIMEOUT) res.raise_for_status() parsed = True status = 200 diff --git a/dzgui/const/constants.py b/dzgui/const/constants.py index 294bc01..29d72d5 100644 --- a/dzgui/const/constants.py +++ b/dzgui/const/constants.py @@ -4,7 +4,7 @@ UDP_PORT = 27016 VM_FILE = "/proc/sys/vm/max_map_count" MIN_COUNT = 1048576 -REQUEST_TIMEOUT = 5 +REQUEST_TIMEOUT = 10 APPNAME_DAYZ = "DayZ" APPNAME_DAYZ_EXP = "DayZ Experimental" diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 367575c..1afbc93 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -381,7 +381,7 @@ class Controller(GObject.GObject): res = future.result() if res.status != 200 or not res.parsed: # TODO: pop warning dialog - self.push_data(None) + self.push_data(None, FilterMode.INITIAL) return j = res.json serv += j["response"]["servers"] @@ -609,22 +609,22 @@ class Controller(GObject.GObject): # TODO: may be superfluous treeview.grab_focus() self.destroy_on_idle() + if len(treeview.filter_man.get_model()) == 0: + # TODO: different dialogs for server tab contexts + # TODO: if history/favorites is empty, don't even trigger a call + # TODO: add proper string for this dialog + dialog = ExceptionDialog(self, "API TIMEOUT") + dialog.run() def push_data(self, data: tuple, mode: FilterMode) -> None: - #def cleanup(): - # # TODO: rename signal - # self.mediator.statusbar.emit("server_page_changed", context) - # # TODO: may be superfluous - # treeview.grab_focus() - # self.destroy_on_idle() treeview = self.get_active_treeview() context = self.get_active_context() + manager = treeview.get_filter_man() if data is None: insert = None else: - manager = treeview.get_filter_man() # TODO: consolidate into filter manager if mode == FilterMode.INITIAL: manager.set_control(data) @@ -639,32 +639,6 @@ class Controller(GObject.GObject): def highlight_stale(self) -> None: self.colorize_mods() - # def call_on_thread(self, func: Callable, *args) -> None: - # self.wait_dialog = WaitDialog(self, strings.dialog.fetching) - # self.wait_dialog.show_all() - # thread = threading.Thread(target=func, args=args) - # thread.start() - - #def filter_cleanup(self) -> None: - # tv = self.get_active_treeview() - # m = tv.filter_man.get_model() - # # TODO: model should not be getting updated in this thread - # # delegate to self.push_data() - # # keeping in mind that server refresh button has to wipe control model - # tv.set_model(m) - - #@call_on_thread - #def filter_threaded(self, mode: FilterMode, label: str) -> None: - # tv = self.get_active_treeview() - # tv.filter_man.filter(mode, label) - # self.set_callback(self.filter_cleanup) - # self.destroy_on_idle() - - #def filter_model(self, mode: FilterMode, label: str) -> None: - # tv = self.get_active_treeview() - # tv.set_model(None) - # self.filter_threaded(mode, label) - def get_callback(self) -> Callable | None: return self.callback["func"] diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 86ed068..4a21c22 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -79,6 +79,7 @@ class FilterPanel(Gtk.Box): self.filters_label = BoldLabel("Filters") self.keyword_entry = Gtk.Entry() + # TODO :strings self.keyword_entry.set_placeholder_text("Filter by keyword") self.keyword_entry.connect("activate", self._on_keyword_enter) self.keyword_entry.connect("key-press-event", self._on_keyword_keypress) diff --git a/dzgui/views/dialogs/generic.py b/dzgui/views/dialogs/generic.py index 367b700..b93901b 100644 --- a/dzgui/views/dialogs/generic.py +++ b/dzgui/views/dialogs/generic.py @@ -1,5 +1,5 @@ import textwrap -from typing import Any, Literal, Self, TYPE_CHECKING +from typing import Literal, Self, TYPE_CHECKING from dzgui.const.constants import NO_EXPAND, NO_FILL, EXPAND, FILL from dzgui.const.enum import Popup, ButtonType, NotebookPage @@ -7,6 +7,7 @@ from dzgui.util import strings from dzgui.views.components.buttons import ClipboardButton import gi + gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 @@ -29,12 +30,13 @@ if TYPE_CHECKING: class GenericDialog(Gtk.MessageDialog): - def __init__(self, + def __init__( + self, controller: "Controller", text: str, mtype: Gtk.MessageType, buttons: Gtk.ButtonsType, - secondary: str + secondary: str, ) -> None: super().__init__( transient_for=controller.mediator.window, @@ -101,10 +103,10 @@ class WaitDialog(GenericDialog): spinner.start() # FIXME: center on parent window - #self.show_all() + # self.show_all() def _on_dialog_delete( - self, response_id: Gtk.ResponseType, event: Gdk.Event + self, response_id: Gtk.ResponseType, event: Gdk.Event ) -> Literal[True]: """ Prevent manual dialog destruction @@ -143,6 +145,7 @@ class ExceptionDialog(GenericDialog): dialog = ExceptionDialog(Controller, trace) dialog.run() """ + def __init__(self, controller: "Controller", trace: str): super().__init__( controller=controller, @@ -154,15 +157,11 @@ class ExceptionDialog(GenericDialog): # NOTE: box expands to end of content area scrollable = Gtk.ScrolledWindow( - propagate_natural_height=True, - max_content_height=500 + propagate_natural_height=True, max_content_height=500 ) box = Gtk.Box(hexpand=True, vexpand=True, orientation=Gtk.Orientation.VERTICAL) textview = Gtk.TextView( - wrap_mode=Gtk.WrapMode.WORD, - editable=False, - left_margin=10, - right_margin=10 + wrap_mode=Gtk.WrapMode.WORD, editable=False, left_margin=10, right_margin=10 ) textview.set_buffer(Gtk.TextBuffer(text=trace)) box.pack_start(textview, EXPAND, FILL, 10) @@ -176,16 +175,18 @@ class ExceptionDialog(GenericDialog): copy_button = ClipboardButton(controller, trace) self.add_action_widget(copy_button, Gtk.ResponseType.NONE) self.add_button("OK", Gtk.ResponseType.OK) - action_area = self.get_action_area() - self.show_all() + self.show_all() + self.action_area.get_children()[1].grab_focus() self.connect("response", self._on_response) - def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None: + def _on_response( + self, dialog: Self, response: Gtk.ResponseType + ) -> None | Literal[True]: match response: case Gtk.ResponseType.OK: self.destroy() case Gtk.ResponseType.NONE: - return True # type: ignore + return True case Gtk.ResponseType.DELETE_EVENT: self.destroy() diff --git a/dzgui/views/trees/tree_base.py b/dzgui/views/trees/tree_base.py index ac1f47b..39fdbf5 100644 --- a/dzgui/views/trees/tree_base.py +++ b/dzgui/views/trees/tree_base.py @@ -80,8 +80,7 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> None: if is_navkey(event.keyval): - # TODO: if model is None - if len(self.get_model()) < 2: + if self.get_model() is None: return if self.sel_blocked is False: self.controller.suppress_signal( @@ -108,7 +107,6 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore return if len(self.get_model()) < 2: return - if is_navkey(event.keyval): if self.sel_blocked is True: self.controller.suppress_signal(