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"
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
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