feat: Microsoft Store update notification for MSIX users#109
Merged
Conversation
Adds app/store_updater.py with helpers to detect MSIX-packaged execution (PACKAGE_FULL_NAME env var or \WindowsApps\ exe path) and to query the public DisplayCatalog API for the latest published version of listing 9P70QGR8BSMZ. Network/parse failures are logged and return None so a missing connectivity check never blocks app start-up.
MSIX/Microsoft Store users previously short-circuited in
is_system_install() so check_for_update() returned None and they
saw no update notification at all. They now flow through a new
branch in check_for_update() that queries the Store via
app.store_updater and returns a {"msix": True, "latest_version",
"deep_link"} payload for the UI layer to render a Store-redirect
dialog (the NSIS installer cannot run in the AppContainer sandbox
and would create a parallel installation).
Removes the WindowsApps early-return in _check_for_updates_async
so MSIX users now flow through check_for_update(). _notify_update
branches on the new {"msix": True, ...} payload and routes to a
new _notify_store_update helper that opens a QMessageBox with
Open/Later buttons; Open launches the ms-windows-store://pdp/
deep-link via QDesktopServices. _show_update_dialog also branches
so the user can re-trigger the Store dialog from the update button.
Adds update.store.title, update.store.message (with {version}
placeholder), update.store.btn.open and update.store.btn.later
for the new Microsoft Store update-notification dialog. Parity
maintained across en/pt/es/fr/de/zh/it/nl.
27 new tests cover MSIX install detection (env var + path), version parsing, DisplayCatalog response parsing (highest version wins, trailing .0 stripped), network/parse failure paths, has-update vs same vs older comparisons, deep-link format, source-level guards that app/updater.py + app/window.py branch on the MSIX payload, and i18n parity for the four new update.store.* keys across all eight languages.
…polish Round 11 review caught a notification-spam regression: the Store update dialog fires on every startup until the user upgrades, which in MSIX (where Store propagation lags 1-7 days behind our releases) means users see the same dialog daily. Changes: - New dismissed_store_version persistent flag in config.json managed via get_dismissed_store_version / set_dismissed_store_version in app/i18n.py. - check_for_store_update returns (False, None) if user already dismissed the current latest (or newer). - _notify_store_update now offers 3 buttons: Open Store (opens + dismisses), Later (re-show), Don't show again for this version (persistent dismiss). Docstring corrected (was claimed non-blocking). - New i18n key update.store.btn.dismiss x 8 languages. - Defensive version compare: pad both sides to 4-tuple to avoid the latent tuple-length asymmetry bug (1.13.16.0 > 1.13.16). - Removed unused 'import io' and 'import pytest' from tests. - Documented PACKAGE_FULL_NAME detection as best-effort fallback. - Added _log.debug of raw response snippet on parse failure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
| # because data may be unrepresentable. | ||
| try: | ||
| _log.debug("Raw response prefix: %s", str(data)[:500]) | ||
| except Exception: |
| t("update.store.btn.open"), | ||
| QMessageBox.ButtonRole.AcceptRole, | ||
| ) | ||
| later_btn = box.addButton( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Detects when PDFApps is running as an MSIX install (Microsoft Store) and shows a Store deep-link dialog when a newer version is published, instead of running the inappropriate NSIS self-updater that MSIX sandbox would block.
Why
Until now, MSIX users hit the GitHub Releases self-updater path even though:
Architecture
app/store_updater.py (new, ~170 LOC):
is_msix_install()— detect viaPACKAGE_FULL_NAMEenv (best-effort) OR\WindowsApps\in executable path (reliable).get_store_version()— querydisplaycatalog.mp.microsoft.com/v7.0/products?bigIds=9P70QGR8BSMZ, parsePackages[*].Version, return highest (trailing-zero stripped).check_for_store_update()— returns (has_update, latest) with defensive 4-tuple version compare and persistent dismiss guard.store_deep_link()—ms-windows-store://pdp/?productid=9P70QGR8BSMZ.app/updater.py:
is_system_installno longer short-circuits on Windows (MSIX detected separately);check_for_updatereturns{"msix": True, ...}payload for MSIX users before falling back to GitHub.app/window.py:
_check_for_updates_asyncno longer hard-skips on WindowsApps;_notify_updatebranches onpayload.get("msix"); new_notify_store_updateshows 3-button dialog (Open Store / Later / Don't show again for this version) with deep-link viaQDesktopServices.app/i18n.py:
get_dismissed_store_version/set_dismissed_store_versionpersist user dismissal inconfig.json.Anti-spam
Without dismiss persistence, the dialog would fire every startup until the Store propagated the new version (days). 3-button UX:
config.dismissed_store_version; only re-prompts when an EVEN newer version is publishedi18n
5 new keys x 8 languages (en/pt/es/fr/de/it/nl/zh):
update.store.titleupdate.store.message(with{version}placeholder)update.store.btn.openupdate.store.btn.laterupdate.store.btn.dismissTests
tests/test_store_updater.py— 35 tests, all passing:{version}placeholder)Adversarial review
The review caught a notification-spam blocker: original implementation re-fired the dialog every startup. Fixed with persistent dismiss (commit
0bd62d7). Also addressed: defensive version compare to avoid latent tuple-length asymmetry, docstring inconsistency (execis modal, not "non-blocking"), unused test imports, and added debug log of raw response on parse failure.Validation
APP_VERSIONbumpurllib.request,json,QtGui.QDesktopServicesare stdlib/PySide6 bundled)Manual QA before release
Recommend testing on a real MSIX install (install via Microsoft Store on a Windows machine, then patch
APP_VERSIONto an older value locally) to confirm:is_msix_install()returns True under\WindowsApps\config.jsonhasdismissed_store_version)