Added ui changes to modals, pre-loader and some fixes for other ui elements and performance improvements all over.
This commit is contained in:
@@ -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"
|
||||
MAX_NEWS_BYTES = 2 * 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"
|
||||
|
||||
|
||||
@@ -241,26 +243,41 @@ def _parse_latest_broadcast(payload: bytes) -> Broadcast:
|
||||
value = json.loads(payload.decode("utf-8-sig"))
|
||||
if not isinstance(value, list) or not value or not isinstance(value[0], dict):
|
||||
raise NewsError("broadcast response does not contain a latest broadcast")
|
||||
latest = value[0]
|
||||
author = latest.get("author")
|
||||
content = latest.get("content")
|
||||
return _broadcast_from_mapping(value[0])
|
||||
|
||||
|
||||
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"))
|
||||
if (
|
||||
not isinstance(author, str)
|
||||
or not author.strip()
|
||||
or not isinstance(content, str)
|
||||
or not content.strip()
|
||||
author is None
|
||||
or content is None
|
||||
or url is None
|
||||
):
|
||||
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(
|
||||
author=author.strip(),
|
||||
content=content.strip(),
|
||||
published_at=_format_timestamp(latest.get("timestamp")),
|
||||
author=author,
|
||||
content=content,
|
||||
published_at=published_at,
|
||||
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:
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
||||
return None
|
||||
@@ -288,10 +305,16 @@ def _image_url(value: object) -> str | None:
|
||||
def _broadcast_url(value: object) -> str | None:
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
return None
|
||||
parsed = urlparse(value)
|
||||
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
||||
url = value.strip()
|
||||
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 value.strip()
|
||||
return url
|
||||
|
||||
|
||||
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:
|
||||
try:
|
||||
value = json.loads(path.read_text(encoding="utf-8"))
|
||||
return Broadcast(**value) if isinstance(value, dict) else None
|
||||
except (OSError, UnicodeError, TypeError, json.JSONDecodeError):
|
||||
return _broadcast_from_mapping(value) if isinstance(value, dict) else None
|
||||
except (OSError, UnicodeError, NewsError, json.JSONDecodeError):
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -304,6 +304,7 @@ def create_window_chrome(
|
||||
*,
|
||||
title: str,
|
||||
subtitle: str,
|
||||
window_icon: Any | None = None,
|
||||
show_title_bar: bool = True,
|
||||
show_close: bool = True,
|
||||
show_minimize: bool = False,
|
||||
@@ -347,17 +348,39 @@ def create_window_chrome(
|
||||
title_bar.pack_propagate(False)
|
||||
tk_accent = tk.Frame(title_bar, background="#d98927", width=3)
|
||||
tk_accent.pack(side="left", fill="y")
|
||||
mark = tk.Canvas(
|
||||
title_bar,
|
||||
width=22,
|
||||
height=22,
|
||||
background="#17100d",
|
||||
borderwidth=0,
|
||||
highlightthickness=0,
|
||||
cursor="fleur",
|
||||
)
|
||||
mark.create_oval(3, 3, 19, 19, outline="#d98927", width=1)
|
||||
mark.create_text(11, 11, text="PT", fill="#f5d18b", font=("Georgia", 6, "bold"))
|
||||
title_icon: Any | None = None
|
||||
if window_icon is not None:
|
||||
try:
|
||||
factor = icon_subsample_factor(
|
||||
window_icon.width(),
|
||||
window_icon.height(),
|
||||
20,
|
||||
)
|
||||
title_icon = window_icon.subsample(factor, factor)
|
||||
except (tk.TclError, ValueError):
|
||||
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))
|
||||
identity = tk.Frame(title_bar, background="#17100d", cursor="fleur")
|
||||
identity.pack(side="left", fill="y")
|
||||
@@ -440,6 +463,7 @@ def create_dashboard_chrome(
|
||||
*,
|
||||
title: str,
|
||||
subtitle: str,
|
||||
window_icon: Any | None = None,
|
||||
) -> DashboardChrome:
|
||||
"""Install the draggable frame and full controls used by the dashboard."""
|
||||
|
||||
@@ -447,6 +471,7 @@ def create_dashboard_chrome(
|
||||
root,
|
||||
title=title,
|
||||
subtitle=subtitle,
|
||||
window_icon=window_icon,
|
||||
show_close=True,
|
||||
show_minimize=True,
|
||||
allow_close=True,
|
||||
|
||||
712
ptlinux/ui.py
712
ptlinux/ui.py
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user