Compare commits

..
Author SHA1 Message Date
aclist 3677b01fdc fix: exception handling 2026-11-09 22:25:13 +09:00
aclist 4f6223f90b fix: strip invalid values from versions file 2026-11-09 22:24:26 +09:00
aclist 441066a1a0 chore: update comments 2026-11-09 22:23:37 +09:00
aclist fa1ac8fa31 chore: update imports 2026-11-09 22:23:27 +09:00
25 changed files with 141 additions and 93 deletions

No files matched your search

+13 -4
View File
@@ -2,10 +2,10 @@ import hashlib
import logging
import re
from concurrent.futures import wait
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor, wait
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from dzgui.api.steam import get_app_path
from dzgui.api.servers import get_rules, fqip_to_record
@@ -18,12 +18,19 @@ from dzgui.const.constants import (
from dzgui.config.query import lookup
from dzgui.const.enum import Preferences
from typing import Any
from dzgui.init.migrate import clean_legacy_versions_file
logger = logging.getLogger(APP_NAME)
@dataclass
class ModMeta:
protocol: str
published_id: str
name: str
timestamp: str
def get_local_mod_ids(steam_path: Path) -> list[int]:
workshop_path = get_local_mod_path(steam_path)
mods = get_local_mods(workshop_path)
@@ -125,6 +132,8 @@ def remove_stale_signatures(
versions.touch()
return
clean_legacy_versions_file(versions)
path = lookup(config, Preferences.DEFAULT)
steam_path = Path(path)
if ids is None:
+4 -1
View File
@@ -18,8 +18,8 @@ from dzgui.config.query import lookup
from dzgui.config.userprefs import UserPrefs
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
from dzgui.init.migrate import (
has_new_config,
copy_state_files,
has_new_config,
migrate_cols_file,
)
from dzgui.init.flock import lock_acquire
@@ -158,6 +158,9 @@ def build_gui(version: str, is_debug: bool) -> App | None:
f.truncate(0)
if _is_steam_deck is False:
# TODO: sudo escalation dialog
# count = get_map_count()
# TODO: move into module
if FLATPAK_ROOT_PATH in lookup(XDG.config, Preferences.DEFAULT):
has_client = has_flatpak_client()
else:
+2 -1
View File
@@ -6,7 +6,8 @@ if TYPE_CHECKING:
from dzgui.util.ip import Coords
# NOTE: mutable dataclass, 'use_miles' key changes on demand
# NOTE: mutable dataclass: 'use_miles' key changes on demand
# 'frozen' cannot be True
@dataclass(slots=True)
class UserPrefs:
is_steam_deck: bool
+22 -1
View File
@@ -1,12 +1,21 @@
import json
import logging
import re
import shutil
from pathlib import Path
from typing import Any
from dzgui.const.constants import LEGACY_CONFIG_PATH, LEGACY_COLS_PATH, LEGACY_IPS_PATH
from dzgui.const.constants import (
APP_NAME,
LEGACY_CONFIG_PATH,
LEGACY_COLS_PATH,
LEGACY_IPS_PATH,
)
from dzgui.config.convert import rc2json
from dzgui.util._json import read_json, write_json
logger = logging.getLogger(APP_NAME)
def migrate_legacy_conf(config: Path) -> Any:
old_conf = Path.home() / LEGACY_CONFIG_PATH
@@ -70,3 +79,15 @@ def copy_ipdb(ips_path: Path) -> None:
return
# TODO: ensure that month file is copied
shutil.copy(legacy, ips_path)
def clean_legacy_versions_file(versions: Path) -> None:
# NOTE: handling for #523
try:
pat = r"^(\d+),(\d+)$"
lines = versions.read_text().splitlines()
clean = [line for line in lines if re.match(pat, line)]
concat = "\n".join(clean)
versions.write_text(concat)
except Exception as e:
logger.warning(e)
+5 -3
View File
@@ -48,7 +48,7 @@ from dzgui.strings.dialogs import (
waiting_for_directories,
)
from dzgui.strings import kb
from dzgui.strings.server_mods import checkmark, resync, invalid_mod
from dzgui.strings.server_mods import checkmark, resync
from dzgui.util.format import format_exception, format_mib
from dzgui.util.strings import dialog, server_timeout
from dzgui.util.symlink import rebuild_symlinks
@@ -197,8 +197,9 @@ class ConnectionManager:
is_last = self.is_last_server()
# TODO: strings
invalid_mods = [
(mod[0], mod[1]) for mod in remote_mods if mod[2] == invalid_mod
(mod[0], mod[1]) for mod in remote_mods if mod[2] == "Invalid mod"
]
prereqs = Prerequisites(
@@ -276,7 +277,8 @@ class ConnectionManager:
for mod in alpha_mods:
# NOTE: if the mod is neither synched or out of date, it is a malformed mod
if mod[2] == "":
mod[2] = invalid_mod
# TODO: strings
mod[2] = "Invalid mod"
return alpha_mods, missing_mods
+9 -3
View File
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING
from dzgui.model.model_factory import ModelFactory
from dzgui.util import strings
from dzgui.strings.filters import all_maps
from dzgui.util.strings import all_maps
if TYPE_CHECKING:
from dzgui.model.model_factory import FastInsertListStore
@@ -106,10 +106,16 @@ class FilterManager:
self.reinit_map_store()
maps.sort()
clean = [m for m in maps if m != all_maps]
for m in clean:
for m in maps:
# TODO: strings
if m == "All maps":
continue
self.append_map([m])
# TODO: currently unused
def get_unique_maps(self) -> list[str]:
return [row[0] for row in self.map_store if row[0] != "All maps"]
def get_all_filters(self) -> tuple:
map_name = self.get_active_map_name()
enabled = self.get_filters()
+1 -2
View File
@@ -5,7 +5,6 @@ from typing import Any, TYPE_CHECKING, Union
from dzgui.const.enum import FilterMode
from dzgui.model.model_factory import ModelFactory
from dzgui.util import strings
from dzgui.strings import filters as str_filters
if TYPE_CHECKING:
from dzgui.managers.filter import FilterManager
@@ -176,7 +175,7 @@ class ProxyModelManager:
rows = self.filtered
sel_map = self.filter_man.get_active_map_name()
if sel_map == str_filters.all_maps:
if sel_map == strings.all_maps:
return rows
rows = [row for row in rows if row[1] == sel_map]
-14
View File
@@ -1,16 +1,2 @@
clipboard_button = "Copy to clipboard"
clipboard_ip_button = "Copy IP to clipboard"
dock_button_collapse = "Collapse the connection panel"
dock_button_expand = "Expand the connection panel"
workshop = "Steam Workshop"
workshop_tooltip = "Open your Workshop subscriptions"
version_notifier = "Copied!"
version_tooltip = "Click to copy to clipboard"
update_button = "Updates available"
unset_button_label = "Unset"
unset_button_tooltip = "Unset this server as favorite"
-2
View File
@@ -29,5 +29,3 @@ toggle_calc = "Toggle calculator"
server_details = "Server details"
server_message = "Server message"
caught_cancel_signal = "Caught cancel signal, cleaning up"
-4
View File
@@ -1,4 +0,0 @@
all_maps = "All maps"
filters_heading = "Filters"
keyword_placeholder = "Filter by keyword"
map_placeholder = "Filter by map"
-1
View File
@@ -1,2 +1 @@
tab_changed_label = "*"
selectable_tree = "Ctrl-click to select multiple; Shift-click to select a range."
-5
View File
@@ -1,5 +0,0 @@
dialog_title = "Add a short note/reminder"
dialog_subtitle = "Limit 30 chars"
delete_button = "Delete note"
ok_button = "Update note"
current_note = "Current note: '{0}'"
-2
View File
@@ -16,9 +16,7 @@ lan_combo = "LAN"
fullscreen_eventbox = "This option is not available on Steam Deck."
save_button = "Save"
api_placeholder = "Enter API key"
api_failed = "API validation failed"
api_validate = "Validate"
flatpak_label = "Flatpak client"
flatpak_standard = "Standard"
+1 -2
View File
@@ -3,5 +3,4 @@ mod_id = "ID"
up_to_date = "Up to date"
modlist = "Modlist"
checkmark = "✓"
resync = "⟳"
invalid_mod = "Invalid mod"
resync="⟳"
+3
View File
@@ -159,6 +159,9 @@ filter_unofficial = "Unofficial"
filter_modded = "Modded"
filter_unmodded = "Unmodded"
# maps
all_maps = "All maps"
# state
unknown = "Unknown"
none_provided = "None provided"
+6 -4
View File
@@ -106,7 +106,8 @@ class ClipboardButton(IconTextButton):
self.controller = controller
self.connect("clicked", self._on_button_clicked, func)
self.set_tooltip_text(buttons.clipboard_button)
# TODO: strings
self.set_tooltip_text("Copy to clipboard")
def _on_button_clicked(self, button: Self, func: Callable) -> None:
def revert() -> Literal[False]:
@@ -124,14 +125,16 @@ class CopyIpButton(ClipboardButton):
def __init__(self, controller: Union["Controller", None], func: Callable) -> None:
super().__init__(controller, func)
self.set_tooltip_text(buttons.clipboard_ip_button)
# TODO: strings
self.set_tooltip_text("Copy IP to clipboard")
class CloseButton(IconTextButton):
def __init__(self, label: str) -> None:
super().__init__(icon=CLOSE, label=label)
self.set_tooltip_text(buttons.unset_button_tooltip)
# TODO: strings
self.set_tooltip_text("Unset this server as favorite")
class WebButton(IconTextButton):
@@ -249,7 +252,6 @@ class AddButton(IconTextButton):
self.set_tooltip_text(connect_panel.add_tooltip)
# TODO: drop
class LoggerAlertsButton(IconTextButton):
def __init__(self, warnings: int, errors: int) -> None:
warnings_text = ""
+9 -9
View File
@@ -3,11 +3,6 @@ from typing import Self, TYPE_CHECKING
from dzgui.const.constants import CARET_DOWN, CARET_UP, UDP_PORT
from dzgui.model.servers import ServerModelManager
from dzgui.strings import connect_panel
from dzgui.strings.buttons import (
dock_button_collapse,
dock_button_expand,
unset_button_label,
)
from dzgui.util.keys import is_ctrl_mask
from dzgui.views.components.buttons import (
AddButton,
@@ -41,18 +36,21 @@ class DockButton(IconButton):
self.child = child
self.connect("clicked", self._on_click)
self.collapse_text = "Collapse the connection panel"
self.expand_text = "Expand the connection panel"
self.set_margin_top(10)
self.set_tooltip_text(dock_button_collapse)
self.set_tooltip_text(self.collapse_text)
def collapse(self) -> None:
self.set_image(Icon(CARET_UP))
self.child.hide()
self.set_tooltip_text(dock_button_expand)
self.set_tooltip_text(self.expand_text)
def expand(self) -> None:
self.set_image(Icon(CARET_DOWN))
self.child.show()
self.set_tooltip_text(dock_button_collapse)
self.set_tooltip_text(self.collapse_text)
def _on_click(self, button: Self) -> None:
if self.expanded:
@@ -83,6 +81,7 @@ class LanPanel(Gtk.Frame):
self.scan = Gtk.Button(label=connect_panel.lan_scan_button)
self.scan.connect("clicked", self._on_scan_clicked)
# TODO: strings
self.early_abort = Gtk.CheckButton(label=connect_panel.lan_checkbox)
self.early_abort.set_active(True)
self.early_abort.set_tooltip_text(connect_panel.lan_abort_tooltip)
@@ -190,7 +189,8 @@ class FavPanel(Gtk.Frame):
self.fav_button.connect("clicked", self._on_connect_clicked)
self.copy_button = CopyIpButton(self.controller, self.get_fav_ip)
self.unset_button = CloseButton(unset_button_label)
# TODO: strings
self.unset_button = CloseButton("Unset")
self.unset_button.connect("clicked", self._on_unset_clicked)
if favorite is None:
+4 -3
View File
@@ -4,7 +4,6 @@ from dzgui.api.servers import validate_ip
from dzgui.const.constants import VIEW_CONCEAL, VIEW_REVEAL
from dzgui.util.css import add_class, remove_class
from dzgui.strings import connect_panel
from dzgui.strings.options import api_placeholder, api_validate
from dzgui.strings.errors import api_popover
import gi
@@ -144,8 +143,9 @@ class APIEntry(Gtk.Box):
self.func: Callable | None = None
# TODO: strings
self.entry = Gtk.Entry(
width_chars=60, hexpand=True, placeholder_text=api_placeholder
width_chars=60, hexpand=True, placeholder_text="Enter API key"
)
self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL)
self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True)
@@ -154,7 +154,8 @@ class APIEntry(Gtk.Box):
self.entry.connect("icon-release", self._on_icon_release)
self.entry.connect("activate", self._on_field_activated)
self.submit = Gtk.Button(label=api_validate)
# TODO: strings
self.submit = Gtk.Button(label="Validate")
self.submit.set_sensitive(False)
self.submit.connect("clicked", self._on_submit)
+8 -13
View File
@@ -10,12 +10,7 @@ from dzgui.const.constants import (
SEARCH_ICON,
)
from dzgui.util.format import format_exception
from dzgui.strings.filters import (
all_maps,
filters_heading,
keyword_placeholder,
map_placeholder,
)
from dzgui.util.strings import all_maps
from dzgui.views.components.maps_combo import MapsCombo
from dzgui.views.components.labels import BoldLabel
@@ -47,10 +42,7 @@ class ButtonGrid(Gtk.Grid):
self.checks: list[Gtk.CheckButton] = []
flowbox = Gtk.FlowBox(
halign=Gtk.Align.CENTER,
min_children_per_line=3,
max_children_per_line=3,
selection_mode=Gtk.SelectionMode.NONE,
halign=Gtk.Align.CENTER, min_children_per_line=3, max_children_per_line=3, selection_mode=Gtk.SelectionMode.NONE
)
for check in defaults.keys():
@@ -107,7 +99,8 @@ class ButtonGrid(Gtk.Grid):
class KeywordEntry(Gtk.Entry):
def __init__(self, controller: "Controller") -> None:
super().__init__(placeholder_text=keyword_placeholder)
# TODO :strings
super().__init__(placeholder_text="Filter by keyword")
self.keyword = ""
self.controller = controller
@@ -181,7 +174,8 @@ class FilterPanel(Gtk.Box):
self.keyword_entry = KeywordEntry(self.controller)
self.button_grid = ButtonGrid(self.controller, defaults, tooltips)
self.filters_label = BoldLabel(filters_heading)
# TODO: strings
self.filters_label = BoldLabel("Filters")
self.emitter.connect(
"request_keyword_focus", lambda _: self.keyword_entry.grab_focus()
@@ -201,7 +195,8 @@ class FilterPanel(Gtk.Box):
self.maps_entry: Gtk.Entry = self.maps_combo.get_child() # type: ignore
self.maps_entry.set_completion(completion)
self.maps_entry.set_placeholder_text(map_placeholder)
# TODO: strings
self.maps_entry.set_placeholder_text("Filter by map")
self.maps_entry.connect("changed", self._on_map_completion, True)
self.maps_entry.connect("key-press-event", self._on_map_entry_keypress)
self.maps_entry.connect("activate", self._on_maps_activated)
+5 -4
View File
@@ -13,7 +13,6 @@ from dzgui.const.constants import (
)
from dzgui.const.enum import ServerTab
from dzgui.managers.update import UpdateManager
from dzgui.strings.buttons import update_button, version_notifier, version_tooltip
from dzgui.util.clip import copy_clipboard
from dzgui.util.file import is_writeable
from dzgui.views.components.buttonbox import ButtonBox
@@ -66,7 +65,8 @@ class RightPanel(Gtk.Box):
vexpand=True,
halign=Gtk.Align.END,
valign=Gtk.Align.END,
tooltip_text=version_tooltip,
# TODO: strings
tooltip_text="Click to copy to clipboard",
)
eb = Gtk.EventBox(halign=Gtk.Align.END, valign=Gtk.Align.END)
eb.add(self.version_label)
@@ -78,7 +78,7 @@ class RightPanel(Gtk.Box):
self.pack_start(self.sel_panel, NO_EXPAND, NO_FILL, NO_PADDING)
self.update_button = IconTextButton(
"dialog-information-symbolic", label=update_button
"dialog-information-symbolic", label="Updates available"
)
self.gutter_box = Gtk.Box(
@@ -131,6 +131,7 @@ class RightPanel(Gtk.Box):
return
self.copying = True
version = self.version_label.get_text()
self.version_label.set_text(version_notifier)
# TODO: strings
self.version_label.set_text("Copied!")
copy_clipboard(version)
GLib.timeout_add_seconds(1, revert)
+2 -1
View File
@@ -140,7 +140,8 @@ class WaitDialog(GenericDialog):
self.cur_job += 1
def _on_cancel_clicked(self, button: Gtk.Button) -> None:
GLib.idle_add(self.update_text, dialogs.caught_cancel_signal)
# TODO: strings
GLib.idle_add(self.update_text, "Caught cancel signal, cleaning up")
self.controller.set_cancel_event()
def show_cancel(self, state: bool) -> None:
+19 -11
View File
@@ -1,13 +1,12 @@
from typing import Self, TYPE_CHECKING
from dzgui.strings import notes
from dzgui.views.components.entry import ValidatedEntry
from dzgui.views.dialogs.generic import GenericDialog
import gi
import gi # noqa E402
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk, Gtk # noqa E402
if TYPE_CHECKING:
from dzgui.controllers.mc import Controller
@@ -15,20 +14,23 @@ if TYPE_CHECKING:
class NoteDialog(GenericDialog):
def __init__(self, controller: "Controller"):
# TODO: add to strings
title = "Add a short note/reminder."
secondary = "Limit 30 chars"
super().__init__(
controller=controller,
text=notes.dialog_title,
text=title,
buttons=Gtk.ButtonsType.NONE,
mtype=Gtk.MessageType.INFO,
secondary=notes.dialog_subtitle,
secondary=secondary,
)
self.controller = controller
delete_button = Gtk.Button(label=notes.delete_button)
delete_button = Gtk.Button(label="Delete note")
delete_button.set_sensitive(False)
self.add_action_widget(delete_button, Gtk.ResponseType.CANCEL)
self.ok = self.add_button(notes.ok_button, Gtk.ResponseType.OK)
self.ok = self.add_button("Update note", Gtk.ResponseType.OK)
self.ok.grab_default()
self.entry = ValidatedEntry(controller, self.validate)
@@ -40,7 +42,7 @@ class NoteDialog(GenericDialog):
self.entry.set_text(note)
if len(note) > 0:
delete_button.set_sensitive(True)
self.format_secondary_text(notes.current_note.format(note))
self.format_secondary_text(f"Current note: '{note}'")
elif len(note) == 0:
self.ok.set_sensitive(False)
@@ -53,6 +55,9 @@ class NoteDialog(GenericDialog):
self.entry.connect("changed", self._on_text_changed)
def _on_text_changed(self, entry: Gtk.Entry) -> None:
"""
Called in conjunction with signal callback in the parent class
"""
if len(entry.get_text()) == 0:
self.ok.set_sensitive(False)
@@ -66,11 +71,14 @@ class NoteDialog(GenericDialog):
def _on_response(self, dialog: Self, response: Gtk.ResponseType) -> None:
match response:
case Gtk.ResponseType.DELETE_EVENT:
self.destroy()
case Gtk.ResponseType.OK:
note = self.entry.get_text()
self.controller.add_note(note)
self.destroy()
case Gtk.ResponseType.CANCEL:
self.controller.delete_note()
case _:
pass
self.destroy()
self.destroy()
case Gtk.ResponseType.CANCEL | Gtk.ResponseType.CLOSE:
self.destroy()
+1 -1
View File
@@ -14,7 +14,7 @@ from dzgui.const.constants import (
HERO_PATH,
FLATPAK_ROOT_PATH,
LEGACY_CONFIG_PATH,
TMP_PATH,
TMP_PATH
)
from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP
+2 -2
View File
@@ -5,7 +5,6 @@ from typing import Self, TYPE_CHECKING
from dzgui.const.constants import APP_NAME
from dzgui.const.enum import ContextMenuGroup, ServerTab
from dzgui.views.trees.tree_servers import ServerTreeView
from dzgui.strings.generic import tab_changed_label
from dzgui.util.strings import server_labels
import gi
@@ -148,7 +147,8 @@ class ServerNotebook(Gtk.ScrolledWindow):
label = self.notebook.get_tab_label_text(child)
if label is not None:
text = label.strip(tab_changed_label)
# TODO: strings
text = label.strip("*")
self.notebook.set_tab_label_text(child, text)
tree = child.get_tree()
+25
View File
@@ -0,0 +1,25 @@
from typing import TYPE_CHECKING
from dzgui.init.migrate import clean_legacy_versions_file
if TYPE_CHECKING:
from pathlib import Path
def test_null_signature_stripping(tmp_path: "Path") -> None:
tmp = tmp_path.joinpath("dzg.versions")
lines = [
"1111,2222",
"3333,4444",
"5555,null",
"foo",
",4555",
"1111",
" ",
",,,",
"-1",
]
expect = ["1111,2222", "3333,4444"]
tmp.write_text("\n".join(lines))
clean_legacy_versions_file(tmp)
assert expect == tmp.read_text().splitlines()