Added ui changes to modals, pre-loader and some fixes for other ui elements and performance improvements all over.

This commit is contained in:
2026-08-31 23:23:37 +02:00
parent 99982b12d9
commit dadae8f60a
3 changed files with 597 additions and 215 deletions

View File

@@ -20,6 +20,8 @@ OFFICIAL_LOGO_URL = "https://pristontale.eu/public/img/pristontale/logo/logo_sma
BROADCAST_ENDPOINT = "http://5.135.35.188:90/broadcasts" BROADCAST_ENDPOINT = "http://5.135.35.188:90/broadcasts"
MAX_NEWS_BYTES = 2 * 1024 * 1024 MAX_NEWS_BYTES = 2 * 1024 * 1024
MAX_NEWS_IMAGE_BYTES = 12 * 1024 * 1024 MAX_NEWS_IMAGE_BYTES = 12 * 1024 * 1024
MAX_BROADCAST_AUTHOR_CHARACTERS = 100
MAX_BROADCAST_CONTENT_CHARACTERS = 2000
PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n" PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"
@@ -241,26 +243,41 @@ def _parse_latest_broadcast(payload: bytes) -> Broadcast:
value = json.loads(payload.decode("utf-8-sig")) value = json.loads(payload.decode("utf-8-sig"))
if not isinstance(value, list) or not value or not isinstance(value[0], dict): if not isinstance(value, list) or not value or not isinstance(value[0], dict):
raise NewsError("broadcast response does not contain a latest broadcast") raise NewsError("broadcast response does not contain a latest broadcast")
latest = value[0] return _broadcast_from_mapping(value[0])
author = latest.get("author")
content = latest.get("content")
def _broadcast_from_mapping(latest: dict[object, object]) -> Broadcast:
author = _bounded_text(latest.get("author"), MAX_BROADCAST_AUTHOR_CHARACTERS)
content = _bounded_text(latest.get("content"), MAX_BROADCAST_CONTENT_CHARACTERS)
url = _broadcast_url(latest.get("url")) url = _broadcast_url(latest.get("url"))
if ( if (
not isinstance(author, str) author is None
or not author.strip() or content is None
or not isinstance(content, str)
or not content.strip()
or url is None or url is None
): ):
raise NewsError("latest broadcast is incomplete") raise NewsError("latest broadcast is incomplete")
published_at = (
_format_timestamp(latest.get("timestamp"))
if "timestamp" in latest
else _bounded_text(latest.get("published_at"), 64)
)
return Broadcast( return Broadcast(
author=author.strip(), author=author,
content=content.strip(), content=content,
published_at=_format_timestamp(latest.get("timestamp")), published_at=published_at,
url=url, url=url,
) )
def _bounded_text(value: object, maximum_characters: int) -> str | None:
if not isinstance(value, str):
return None
text = value.strip()
if not text or len(text) > maximum_characters:
return None
return text
def _format_timestamp(value: object) -> str | None: def _format_timestamp(value: object) -> str | None:
if isinstance(value, bool) or not isinstance(value, (int, float)): if isinstance(value, bool) or not isinstance(value, (int, float)):
return None return None
@@ -288,10 +305,16 @@ def _image_url(value: object) -> str | None:
def _broadcast_url(value: object) -> str | None: def _broadcast_url(value: object) -> str | None:
if not isinstance(value, str) or not value.strip(): if not isinstance(value, str) or not value.strip():
return None return None
parsed = urlparse(value) url = value.strip()
if parsed.scheme not in {"http", "https"} or not parsed.netloc: parsed = urlparse(url)
if (
parsed.scheme.casefold() != "https"
or not parsed.netloc
or parsed.username is not None
or parsed.password is not None
):
return None return None
return value.strip() return url
def _read_cache(path: Path) -> tuple[NewsArticle, ...]: def _read_cache(path: Path) -> tuple[NewsArticle, ...]:
@@ -320,8 +343,8 @@ def _write_cache(path: Path, articles: tuple[NewsArticle, ...]) -> None:
def _read_broadcast_cache(path: Path) -> Broadcast | None: def _read_broadcast_cache(path: Path) -> Broadcast | None:
try: try:
value = json.loads(path.read_text(encoding="utf-8")) value = json.loads(path.read_text(encoding="utf-8"))
return Broadcast(**value) if isinstance(value, dict) else None return _broadcast_from_mapping(value) if isinstance(value, dict) else None
except (OSError, UnicodeError, TypeError, json.JSONDecodeError): except (OSError, UnicodeError, NewsError, json.JSONDecodeError):
return None return None

View File

@@ -304,6 +304,7 @@ def create_window_chrome(
*, *,
title: str, title: str,
subtitle: str, subtitle: str,
window_icon: Any | None = None,
show_title_bar: bool = True, show_title_bar: bool = True,
show_close: bool = True, show_close: bool = True,
show_minimize: bool = False, show_minimize: bool = False,
@@ -347,17 +348,39 @@ def create_window_chrome(
title_bar.pack_propagate(False) title_bar.pack_propagate(False)
tk_accent = tk.Frame(title_bar, background="#d98927", width=3) tk_accent = tk.Frame(title_bar, background="#d98927", width=3)
tk_accent.pack(side="left", fill="y") tk_accent.pack(side="left", fill="y")
mark = tk.Canvas( title_icon: Any | None = None
title_bar, if window_icon is not None:
width=22, try:
height=22, factor = icon_subsample_factor(
background="#17100d", window_icon.width(),
borderwidth=0, window_icon.height(),
highlightthickness=0, 20,
cursor="fleur", )
) title_icon = window_icon.subsample(factor, factor)
mark.create_oval(3, 3, 19, 19, outline="#d98927", width=1) except (tk.TclError, ValueError):
mark.create_text(11, 11, text="PT", fill="#f5d18b", font=("Georgia", 6, "bold")) title_icon = None
if title_icon is not None:
mark = tk.Label(
title_bar,
image=title_icon,
width=22,
height=22,
background="#17100d",
borderwidth=0,
highlightthickness=0,
cursor="fleur",
)
mark.image = title_icon
else:
mark = tk.Frame(
title_bar,
width=22,
height=22,
background="#17100d",
borderwidth=0,
highlightthickness=0,
cursor="fleur",
)
mark.pack(side="left", padx=(9, 7)) mark.pack(side="left", padx=(9, 7))
identity = tk.Frame(title_bar, background="#17100d", cursor="fleur") identity = tk.Frame(title_bar, background="#17100d", cursor="fleur")
identity.pack(side="left", fill="y") identity.pack(side="left", fill="y")
@@ -440,6 +463,7 @@ def create_dashboard_chrome(
*, *,
title: str, title: str,
subtitle: str, subtitle: str,
window_icon: Any | None = None,
) -> DashboardChrome: ) -> DashboardChrome:
"""Install the draggable frame and full controls used by the dashboard.""" """Install the draggable frame and full controls used by the dashboard."""
@@ -447,6 +471,7 @@ def create_dashboard_chrome(
root, root,
title=title, title=title,
subtitle=subtitle, subtitle=subtitle,
window_icon=window_icon,
show_close=True, show_close=True,
show_minimize=True, show_minimize=True,
allow_close=True, allow_close=True,

File diff suppressed because it is too large Load Diff