Manual update check

This commit is contained in:
Flowseal committed 2026-10-11 18:01:21 +03:00
1 parent 03be9baad1
commit c52714f264
4 files changed
+78 -48

No files matched your search

+2 -2
View File
@@ -32,7 +32,7 @@ class BackgroundTask:
try: try:
results.put((True, work())) results.put((True, work()))
except Exception: except Exception:
log.exception("Connectivity test failed") log.exception("Background task failed")
results.put((False, None)) results.put((False, None))
def poll(): def poll():
@@ -52,5 +52,5 @@ class BackgroundTask:
if not self.closed: if not self.closed:
finish() finish()
threading.Thread(target=run, daemon=True, name="connectivity-test").start() threading.Thread(target=run, daemon=True, name="ui-background-task").start()
self.pending = self.owner.after(50, poll) self.pending = self.owner.after(50, poll)
+3 -1
View File
@@ -67,6 +67,7 @@
"button.open_release": "Open release page", "button.open_release": "Open release page",
"button.start": "Get started", "button.start": "Get started",
"button.update": "Update", "button.update": "Update",
"button.check_updates": "Check for updates",
"button.page": "Page", "button.page": "Page",
"button.close": "Close", "button.close": "Close",
"button.donate": "Donate ♥", "button.donate": "Donate ♥",
@@ -97,7 +98,8 @@
"connectivity.multi_partial": "~ {domain}: working {ok_list}; unreachable {fail_list}", "connectivity.multi_partial": "~ {domain}: working {ok_list}; unreachable {fail_list}",
"updates.status_error": "Could not reach GitHub. Check your network.", "updates.status_error": "Could not reach GitHub. Check your network.",
"updates.status_pending": "Status will appear after the background check on startup.", "updates.status_pending": "Updates have not been checked yet.",
"updates.status_checking": "Checking for updates...",
"updates.status_available": "Version {latest} is available on GitHub (you have {current}).", "updates.status_available": "Version {latest} is available on GitHub (you have {current}).",
"updates.status_ahead": "You have {current} — newer than the latest GitHub release ({latest}).", "updates.status_ahead": "You have {current} — newer than the latest GitHub release ({latest}).",
"updates.status_latest": "Latest known version from GitHub is installed.", "updates.status_latest": "Latest known version from GitHub is installed.",
+3 -1
View File
@@ -67,6 +67,7 @@
"button.open_release": "Открыть страницу релиза", "button.open_release": "Открыть страницу релиза",
"button.start": "Начать", "button.start": "Начать",
"button.update": "Обновить", "button.update": "Обновить",
"button.check_updates": "Проверить обновления",
"button.page": "Страница", "button.page": "Страница",
"button.close": "Закрыть", "button.close": "Закрыть",
"button.donate": "Поддержать ♥", "button.donate": "Поддержать ♥",
@@ -97,7 +98,8 @@
"connectivity.multi_partial": "~ {domain}: работают {ok_list}; недоступны {fail_list}", "connectivity.multi_partial": "~ {domain}: работают {ok_list}; недоступны {fail_list}",
"updates.status_error": "Не удалось связаться с GitHub. Проверьте сеть.", "updates.status_error": "Не удалось связаться с GitHub. Проверьте сеть.",
"updates.status_pending": "Статус появится после фоновой проверки при запуске.", "updates.status_pending": "Обновления ещё не проверялись.",
"updates.status_checking": "Проверка обновлений...",
"updates.status_available": "На GitHub доступна версия {latest} (у вас {current}).", "updates.status_available": "На GitHub доступна версия {latest} (у вас {current}).",
"updates.status_ahead": "У вас {current} — новее последнего релиза на GitHub ({latest}).", "updates.status_ahead": "У вас {current} — новее последнего релиза на GitHub ({latest}).",
"updates.status_latest": "Установлена последняя известная версия с GitHub.", "updates.status_latest": "Установлена последняя известная версия с GitHub.",
+70 -44
View File
@@ -7,7 +7,7 @@ from typing import Any, Callable, Dict, List, Optional, Union
from proxy import __version__, coerce_domain_list from proxy import __version__, coerce_domain_list
from proxy.balancer import balancer from proxy.balancer import balancer
from utils.update_check import RELEASES_PAGE_URL, get_status from utils.update_check import RELEASES_PAGE_URL, get_status, run_check
from ui.background_task import BackgroundTask from ui.background_task import BackgroundTask
from ui.connectivity import ( from ui.connectivity import (
run_cfproxy_multi_test, run_cfproxy_auto_test, run_cfworker_multi_test, run_cfproxy_multi_test, run_cfproxy_auto_test, run_cfworker_multi_test,
@@ -481,7 +481,6 @@ def _create_logging(ctk, frame, theme, cfg, default_config, no_secure_var):
def _create_updates(ctk, frame, theme, cfg, default_config, on_update_click): def _create_updates(ctk, frame, theme, cfg, default_config, on_update_click):
upd_inner = create_config_section(ctk, frame, theme, t("section.updates")) upd_inner = create_config_section(ctk, frame, theme, t("section.updates"))
st = get_status()
check_updates_var = ctk.BooleanVar( check_updates_var = ctk.BooleanVar(
master=frame, value=bool(cfg.get("check_updates", default_config.get("check_updates", True))) master=frame, value=bool(cfg.get("check_updates", default_config.get("check_updates", True)))
) )
@@ -489,50 +488,77 @@ def _create_updates(ctk, frame, theme, cfg, default_config, on_update_click):
upd_cb.pack(anchor="w", pady=(0, 6)) upd_cb.pack(anchor="w", pady=(0, 6))
attach_ctk_tooltip(upd_cb, t("tip.check_updates")) attach_ctk_tooltip(upd_cb, t("tip.check_updates"))
if st.get("error"): status_label = create_label(ctk, upd_inner, theme, "", size=11,
upd_status = t("updates.status_error") justify="left", wraplength=_INNER_W)
elif not st.get("checked"): status_label.pack(anchor="w", pady=(0, 8))
upd_status = t("updates.status_pending") check_task = BackgroundTask(frame.winfo_toplevel())
elif st.get("has_update") and st.get("latest"):
upd_status = t("updates.status_available", latest=st["latest"], current=__version__)
elif st.get("ahead_of_release") and st.get("latest"):
upd_status = t("updates.status_ahead", current=__version__, latest=st["latest"])
else:
upd_status = t("updates.status_latest")
create_label(ctk, upd_inner, theme, upd_status, size=11, def check():
justify="left", wraplength=_INNER_W).pack(anchor="w", pady=(0, 8)) if check_task.running or check_task.closed:
return
check_button.configure(state="disabled")
status_label.configure(text=t("updates.status_checking"))
check_task.start(
lambda: run_check(__version__), lambda _: refresh(),
lambda: check_button.configure(state="normal"),
)
rel_url = (st.get("html_url") or "").strip() or RELEASES_PAGE_URL check_button = ctk.CTkButton(
if st.get("has_update") and on_update_click is not None: upd_inner, text=t("button.check_updates"), height=32,
upd_btn_row = ctk.CTkFrame(upd_inner, fg_color="transparent") font=(theme.ui_font_family, 13), corner_radius=8,
upd_btn_row.pack(fill="x") fg_color=theme.tg_blue, hover_color=theme.tg_blue_hover,
upd_btn_row.grid_columnconfigure(0, weight=1) text_color="#ffffff", command=check,
upd_btn_row.grid_columnconfigure(1, weight=1) )
ctk.CTkButton( upd_btn_row = ctk.CTkFrame(upd_inner, fg_color="transparent")
upd_btn_row, text=t("button.open_release"), height=32, upd_btn_row.pack(fill="x")
font=(theme.ui_font_family, 13), corner_radius=8, upd_btn_row.grid_columnconfigure(0, weight=1)
fg_color=theme.field_bg, hover_color=theme.field_border, release_button = ctk.CTkButton(
text_color=theme.text_primary, border_width=1, upd_btn_row, text=t("button.open_release"), height=32,
border_color=theme.field_border, font=(theme.ui_font_family, 13), corner_radius=8,
command=lambda u=rel_url: webbrowser.open(u), fg_color=theme.field_bg, hover_color=theme.field_border,
).grid(row=0, column=0, sticky="ew", padx=(0, 4)) text_color=theme.text_primary, border_width=1,
ctk.CTkButton( border_color=theme.field_border,
upd_btn_row, text=t("button.update"), height=32, )
font=(theme.ui_font_family, 13, "bold"), corner_radius=8, release_button.grid(row=0, column=0, sticky="ew")
fg_color=theme.tg_blue, hover_color=theme.tg_blue_hover, update_button = ctk.CTkButton(
text_color="#ffffff", upd_btn_row, text=t("button.update"), height=32,
command=on_update_click, font=(theme.ui_font_family, 13, "bold"), corner_radius=8,
).grid(row=0, column=1, sticky="ew", padx=(4, 0)) fg_color=theme.tg_blue, hover_color=theme.tg_blue_hover,
else: text_color="#ffffff", command=on_update_click,
ctk.CTkButton( )
upd_inner, text=t("button.open_release"), height=32,
font=(theme.ui_font_family, 13), corner_radius=8, def refresh():
fg_color=theme.field_bg, hover_color=theme.field_border, st = get_status()
text_color=theme.text_primary, border_width=1, if st.get("error"):
border_color=theme.field_border, status = t("updates.status_error")
command=lambda u=rel_url: webbrowser.open(u), elif not st.get("checked"):
).pack(anchor="w") status = t("updates.status_pending")
elif st.get("has_update") and st.get("latest"):
status = t("updates.status_available", latest=st["latest"], current=__version__)
elif st.get("ahead_of_release") and st.get("latest"):
status = t("updates.status_ahead", current=__version__, latest=st["latest"])
else:
status = t("updates.status_latest")
status_label.configure(text=status)
rel_url = (st.get("html_url") or "").strip() or RELEASES_PAGE_URL
release_button.configure(command=lambda: webbrowser.open(rel_url))
can_update = bool(st.get("has_update") and on_update_click is not None)
upd_btn_row.grid_columnconfigure(1, weight=int(can_update))
release_button.grid_configure(padx=(0, 4) if can_update else 0)
if can_update:
update_button.grid(row=0, column=1, sticky="ew", padx=(4, 0))
else:
update_button.grid_remove()
def sync_manual_check(*_):
if check_updates_var.get():
check_button.pack_forget()
else:
check_button.pack(fill="x", pady=(0, 8), before=upd_btn_row)
check_updates_var.trace_add("write", sync_manual_check)
sync_manual_check()
refresh()
return check_updates_var return check_updates_var