From 741da4311eae1671328a13e170575f6b3dba658e Mon Sep 17 00:00:00 2001 From: Robin Candau Date: Tue, 1 Oct 2024 11:42:09 +0200 Subject: [PATCH 1/3] feat: Show the number and the list of pending updates in systray's tooltip Show the number and the list of pending updates in the systray applet tooltip, shown when hovering the mouse over the systray icon. Closes https://github.com/Antiz96/arch-update/issues/154 --- po/arch-update.pot | 32 ++++++++++++++++-- po/fr.po | 38 +++++++++++++++++++-- src/lib/full_upgrade.sh | 3 +- src/lib/tray.py | 74 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 138 insertions(+), 9 deletions(-) diff --git a/po/arch-update.pot b/po/arch-update.pot index 4de7dcd..85db4e4 100644 --- a/po/arch-update.pot +++ b/po/arch-update.pot @@ -588,11 +588,39 @@ msgstr "" msgid "No service requiring a post upgrade restart found\\n" msgstr "" -#: src/lib/tray.py:123 +#: src/lib/tray.py:124 +msgid "Arch-Update: 'updates' state file isn't found" +msgstr "" + +#: src/lib/tray.py:141 +msgid "Arch-Update: System is up to date" +msgstr "" + +#: src/lib/tray.py:144 +#, python-brace-format +msgid "" +"Arch-Update: 1 update available\n" +"\n" +"{update_list}" +msgstr "" + +#: src/lib/tray.py:149 +#, python-brace-format +msgid "" +"Arch-Update: {updates} updates available\n" +"\n" +"{update_list}" +msgstr "" + +#: src/lib/tray.py:186 msgid "Run Arch-Update" msgstr "" -#: src/lib/tray.py:124 +#: src/lib/tray.py:187 +msgid "Check for updates" +msgstr "" + +#: src/lib/tray.py:188 msgid "Exit" msgstr "" diff --git a/po/fr.po b/po/fr.po index 71dcba9..632bb19 100644 --- a/po/fr.po +++ b/po/fr.po @@ -658,11 +658,45 @@ msgstr "" msgid "No service requiring a post upgrade restart found\\n" msgstr "Aucun service nécessitant un redémarrage suite à la mise à jour n'a été trouvé\\n" -#: src/lib/tray.py:123 +#: src/lib/tray.py:124 +msgid "Arch-Update: 'updates' state file isn't found" +msgstr "Arch-Update : fichier d'état 'updates' non trouvé" + +#: src/lib/tray.py:141 +msgid "Arch-Update: System is up to date" +msgstr "Arch-Update : Le système à jour" + +#: src/lib/tray.py:144 +#, python-brace-format +msgid "" +"Arch-Update: 1 update available\n" +"\n" +"{update_list}" +msgstr "" +"Arch-Update : 1 mise à jour disponible\n" +"\n" +"{update_list}" + +#: src/lib/tray.py:149 +#, python-brace-format +msgid "" +"Arch-Update: {updates} updates available\n" +"\n" +"{update_list}" +msgstr "" +"Arch-Update : {updates} mises à jour disponibles\n" +"\n" +"{update_list}" + +#: src/lib/tray.py:186 msgid "Run Arch-Update" msgstr "Lancer Arch-Update" -#: src/lib/tray.py:124 +#: src/lib/tray.py:187 +msgid "Check for updates" +msgstr "Vérifier les mises à jour" + +#: src/lib/tray.py:188 msgid "Exit" msgstr "Quitter" diff --git a/src/lib/full_upgrade.sh b/src/lib/full_upgrade.sh index 666b68e..bc515e0 100755 --- a/src/lib/full_upgrade.sh +++ b/src/lib/full_upgrade.sh @@ -18,8 +18,9 @@ if [ -n "${proceed_with_update}" ]; then # shellcheck source=src/lib/update.sh source "${libdir}/update.sh" - # Record the date of the last successful update (used by other stages) + # Record the date of the last successful update (used by other stages) and empty the 'updates' state file (which contains the list of pending updates) date +%Y-%m-%d > "${statedir}/last_update_run" + true > "${statedir}/last_updates_check" fi # Source the "orphan_packages" library which displays orphan packages and offers to remove them diff --git a/src/lib/tray.py b/src/lib/tray.py index dbad83f..0024052 100755 --- a/src/lib/tray.py +++ b/src/lib/tray.py @@ -10,6 +10,7 @@ import os import sys import subprocess +import re from PyQt6.QtGui import QIcon, QAction from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu from PyQt6.QtCore import QFileSystemWatcher @@ -26,9 +27,20 @@ ICON_FILE = os.path.join( os.environ['HOME'], '.local', 'state', 'arch-update', 'tray_icon') if not os.path.isfile(ICON_FILE): - log.error("Statefile does not exist: %s", ICON_FILE) + log.error("State icon file does not exist: %s", ICON_FILE) sys.exit(1) +# Find Updates file +UPDATES_FILE = None +if 'XDG_STATE_HOME' in os.environ: + UPDATES_FILE = os.path.join( + os.environ['XDG_STATE_HOME'], 'arch-update', 'last_updates_check') +elif 'HOME' in os.environ: + UPDATES_FILE = os.path.join( + os.environ['HOME'], '.local', 'state', 'arch-update', 'last_updates_check') +if not os.path.isfile(UPDATES_FILE): + log.error("State updates file does not exist: %s", UPDATES_FILE) + # Find translations paths = [] if 'XDG_DATA_DIRS' in os.environ: @@ -79,25 +91,75 @@ class ArchUpdateQt6: """ System Tray using QT6 library """ def file_changed(self): - """ Called when icon file content changes """ + """ Update icon and tooltip on state file content changes """ + self.update_icon() + self.update_tooltip() - contents = "" + def update_icon(self): + """ Update the tray icon based on the icon state file content """ if self.watcher and not self.iconfile in self.watcher.files(): self.watcher.addPath(self.iconfile) + try: with open(self.iconfile, encoding="utf-8") as f: contents = f.readline().strip() except FileNotFoundError: log.error("Statefile Missing") sys.exit(1) + if contents.startswith("arch-update"): icon = QIcon.fromTheme(contents) self.tray.setIcon(icon) + def update_tooltip(self): + """ Update the tooltip with the number / list of pending updates """ + if self.watcher and not self.updatesfile in self.watcher.files(): + self.watcher.addPath(self.updatesfile) + + try: + with open(self.updatesfile, encoding="utf-8") as f: + updates_list = f.readlines() + except FileNotFoundError: + log.error("State updates file missing") + tooltip = _("Arch-Update: 'updates' state file isn't found") + self.tray.setToolTip(tooltip) + return + + # Define a regex pattern to match ANSI escape / color codes + ansi_escape_pattern = re.compile(r'\x1B\[[0-?9;]*[mK]') + + # Remove ANSI escape / color codes and any empty lines, then strip whitespaces + updates_list = [ + ansi_escape_pattern.sub('', update).strip() + for update in updates_list + if update.strip() + ] + + updates_count = len(updates_list) + + if updates_count == 0: + tooltip = _("Arch-Update: System is up to date") + elif updates_count == 1: + update_list = "".join(updates_list) + tooltip = _("Arch-Update: 1 update available\n\n{update_list}").format( + update_list=update_list + ) + else: + update_list = "\n".join(updates_list) + tooltip = _("Arch-Update: {updates} updates available\n\n{update_list}").format( + updates=updates_count, update_list=update_list + ) + + self.tray.setToolTip(tooltip) + def run(self): """ Start arch-update """ arch_update() + def check(self): + """ Check for updates """ + subprocess.run(["arch-update", "--check"], check=False) + def exit(self): """ Close systray process """ sys.exit(0) @@ -106,6 +168,7 @@ def __init__(self, iconfile): """ Start Qt6 System Tray """ self.iconfile = iconfile + self.updatesfile = UPDATES_FILE self.watcher = None # Application @@ -121,17 +184,20 @@ def __init__(self, iconfile): # Menu menu = QMenu() menu_launch = QAction(_("Run Arch-Update")) + menu_check = QAction(_("Check for updates")) menu_exit = QAction(_("Exit")) menu.addAction(menu_launch) + menu.addAction(menu_check) menu.addAction(menu_exit) menu_launch.triggered.connect(self.run) + menu_check.triggered.connect(self.check) menu_exit.triggered.connect(self.exit) self.tray.setContextMenu(menu) # File Watcher - self.watcher = QFileSystemWatcher([self.iconfile]) + self.watcher = QFileSystemWatcher([self.iconfile, self.updatesfile]) self.watcher.fileChanged.connect(self.file_changed) app.exec() From d14a92edcdd5ff7398648c18d341be7602fbdef7 Mon Sep 17 00:00:00 2001 From: Robin Candau Date: Tue, 1 Oct 2024 12:13:13 +0200 Subject: [PATCH 2/3] fix: do not show version changes in systray tooltip if the NoVersion option is used in arch-update.conf --- po/arch-update.pot | 4 ++-- po/fr.po | 4 ++-- src/lib/check.sh | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/po/arch-update.pot b/po/arch-update.pot index 85db4e4..491663b 100644 --- a/po/arch-update.pot +++ b/po/arch-update.pot @@ -16,12 +16,12 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/lib/check.sh:34 src/lib/check.sh:37 +#: src/lib/check.sh:38 src/lib/check.sh:41 #, sh-format msgid "${update_number} update available" msgstr "" -#: src/lib/check.sh:42 src/lib/check.sh:44 +#: src/lib/check.sh:46 src/lib/check.sh:48 #, sh-format msgid "${update_number} updates available" msgstr "" diff --git a/po/fr.po b/po/fr.po index 632bb19..9ff0356 100644 --- a/po/fr.po +++ b/po/fr.po @@ -16,12 +16,12 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/lib/check.sh:34 src/lib/check.sh:37 +#: src/lib/check.sh:38 src/lib/check.sh:41 #, sh-format msgid "${update_number} update available" msgstr "${update_number} mise à jour disponible" -#: src/lib/check.sh:42 src/lib/check.sh:44 +#: src/lib/check.sh:46 src/lib/check.sh:48 #, sh-format msgid "${update_number} updates available" msgstr "${update_number} mises à jour disponibles" diff --git a/src/lib/check.sh b/src/lib/check.sh index 5800dea..2291801 100755 --- a/src/lib/check.sh +++ b/src/lib/check.sh @@ -14,6 +14,10 @@ else update_available=$(checkupdates) fi +if [ -z "${no_version}" ]; then + update_available=$(echo "${update_available}" | awk '{print $1}') +fi + if [ -n "${notif}" ]; then # shellcheck disable=SC2154 echo "${update_available}" > "${statedir}/current_updates_check" From 90d3dd991a47eada9b7ec238f3fa30d70fab7e5e Mon Sep 17 00:00:00 2001 From: Robin Candau Date: Tue, 1 Oct 2024 12:19:18 +0200 Subject: [PATCH 3/3] fix: do not print version changes when NoVersion is used --- src/lib/check.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/check.sh b/src/lib/check.sh index 2291801..96dc10e 100755 --- a/src/lib/check.sh +++ b/src/lib/check.sh @@ -14,7 +14,7 @@ else update_available=$(checkupdates) fi -if [ -z "${no_version}" ]; then +if [ -n "${no_version}" ]; then update_available=$(echo "${update_available}" | awk '{print $1}') fi