From 865296254a66badbb17343cd3dab29684c6f9345 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 26 Oct 2023 09:22:59 +1000 Subject: [PATCH] Tweaks to add-on startup failure screen / update checks - Add a Check for Updates button to the screen - Make the update list screen non-modal, so that other modal pop-ups at startup don't leave the user stuck - When manually checking for updates, update Anki's last check time --- ftl/qt/addons.ftl | 6 ++++-- qt/aqt/addons.py | 46 ++++++++++++++++++++++++++++++++-------------- qt/aqt/main.py | 37 ++++++++++++++++++++++--------------- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/ftl/qt/addons.ftl b/ftl/qt/addons.ftl index 908d9096f..5765292b6 100644 --- a/ftl/qt/addons.ftl +++ b/ftl/qt/addons.ftl @@ -8,8 +8,10 @@ addons-failed-to-load2 = The following add-ons failed to load: { $addons } - Use Tools>Add-ons to check for updates. For add-ons that don't have - an update available, you can disable or delete the add-on to prevent this + They may need to be updated to support this version of Anki. Click the { addons-check-for-updates } button + to see if any updates are available. + + For add-ons that don't have an update available, you can disable or delete the add-on to prevent this message from appearing. addons-startup-failed = Add-on Startup Failed # Shown in the add-on configuration screen (Tools>Add-ons>Config), in the title bar diff --git a/qt/aqt/addons.py b/qt/aqt/addons.py index 8e3327093..63112259d 100644 --- a/qt/aqt/addons.py +++ b/qt/aqt/addons.py @@ -256,18 +256,33 @@ class AddonManager: ) txt = f"# {tr.addons_startup_failed()}\n{error}" html2 = markdown.markdown(txt) - print(html2) - (diag, _) = showText( + box: QDialogButtonBox + (diag, box) = showText( html2, type="html", copyBtn=True, run=False, ) + but = box.addButton( + tr.addons_check_for_updates(), QDialogButtonBox.ButtonRole.ActionRole + ) + but.clicked.connect(self.check_for_updates_after_load_failure) from aqt import mw # calling show immediately appears to crash mw.progress.single_shot(1000, diag.show) + def check_for_updates_after_load_failure(self) -> None: + from aqt import mw + + tooltip(tr.addons_checking()) + + def on_done(log: list[DownloadLogEntry]) -> None: + if not log: + tooltip(tr.addons_no_updates_available()) + + mw.check_for_addon_updates(by_user=True, on_done=on_done) + def onAddonsDialog(self) -> None: aqt.dialogs.open("AddonsDialog", self) @@ -1326,12 +1341,14 @@ class ChooseAddonsToUpdateList(QListWidget): class ChooseAddonsToUpdateDialog(QDialog): + _on_done: Callable[[list[int]], None] + def __init__( self, parent: QWidget, mgr: AddonManager, updated_addons: list[AddonInfo] ) -> None: QDialog.__init__(self, parent) self.setWindowTitle(tr.addons_choose_update_window_title()) - self.setWindowModality(Qt.WindowModality.WindowModal) + self.setWindowModality(Qt.WindowModality.NonModal) self.mgr = mgr self.updated_addons = updated_addons self.setup() @@ -1358,15 +1375,15 @@ class ChooseAddonsToUpdateDialog(QDialog): layout.addWidget(button_box) self.setLayout(layout) - def ask(self) -> list[int]: - "Returns a list of selected addons' ids" - ret = self.exec() + def ask(self, on_done: Callable[[list[int]], None]) -> None: + self._on_done = on_done + self.show() + + def accept(self) -> None: saveGeom(self, "addonsChooseUpdate") self.addons_list_widget.save_check_state() - if ret == QDialog.DialogCode.Accepted: - return self.addons_list_widget.get_selected_addon_ids() - else: - return [] + self._on_done(self.addons_list_widget.get_selected_addon_ids()) + QDialog.accept(self) def fetch_update_info(ids: list[int]) -> list[AddonInfo]: @@ -1463,10 +1480,11 @@ def prompt_to_update( if not prompt_update: return - ids = ChooseAddonsToUpdateDialog(parent, mgr, updated_addons).ask() - if not ids: - return - download_addons(parent, mgr, ids, on_done, client) + def after_choosing(ids: list[int]) -> None: + if ids: + download_addons(parent, mgr, ids, on_done, client) + + ChooseAddonsToUpdateDialog(parent, mgr, updated_addons).ask(after_choosing) # Editing config diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 9f1dfadaa..6c634132d 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -501,7 +501,7 @@ class AnkiQt(QMainWindow): if onsuccess: onsuccess() if not self.safeMode: - self.maybe_check_for_addon_updates(self.setupAutoUpdate) + self.maybe_check_for_addon_updates(self.setup_auto_update) self.maybe_auto_sync_on_open_close(_onsuccess) @@ -963,26 +963,33 @@ title="{}" {}>{}""".format( self.addonManager.loadAddons() def maybe_check_for_addon_updates( - self, on_done: Callable[[], None] | None = None + self, on_done: Callable[[list[DownloadLogEntry]], None] | None = None ) -> None: last_check = self.pm.last_addon_update_check() elap = int_time() - last_check + if elap > 86_400 or self.pm.last_run_version != int_version(): + self.check_for_addon_updates(by_user=False, on_done=on_done) + elif on_done: + on_done([]) + + def check_for_addon_updates( + self, + by_user: bool, + on_done: Callable[[list[DownloadLogEntry]], None] | None = None, + ) -> None: def wrap_on_updates_installed(log: list[DownloadLogEntry]) -> None: self.on_updates_installed(log) - if on_done: - on_done() - - if elap > 86_400 or self.pm.last_run_version != int_version(): - check_and_prompt_for_updates( - self, - self.addonManager, - wrap_on_updates_installed, - requested_by_user=False, - ) self.pm.set_last_addon_update_check(int_time()) - elif on_done: - on_done() + if on_done: + on_done(log) + + check_and_prompt_for_updates( + self, + self.addonManager, + wrap_on_updates_installed, + requested_by_user=by_user, + ) def on_updates_installed(self, log: list[DownloadLogEntry]) -> None: if log: @@ -1413,7 +1420,7 @@ title="{}" {}>{}""".format( # Auto update ########################################################################## - def setupAutoUpdate(self) -> None: + def setup_auto_update(self, _log: list[DownloadLogEntry]) -> None: from aqt.update import check_for_update check_for_update()