Skip to content

Commit 0b436b2

Browse files
committed
Notifications on long run jobs
1 parent 3163da9 commit 0b436b2

File tree

12 files changed

+199
-25
lines changed

12 files changed

+199
-25
lines changed

src/switchcraft/assets/lang/de.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,24 @@
159159
"intune_log_label": "Ausgabe-Log:",
160160
"intune_start_creation": "Starte Paketerstellung...",
161161
"intune_pkg_success": "Paket erstellt in:\n{path}",
162+
"notif_intunewin_complete_title": "IntuneWin bereit",
163+
"notif_intunewin_complete_msg": "Das Paket '{name}' wurde erfolgreich erstellt.",
164+
"notif_analysis_complete_title": "Analyse abgeschlossen",
165+
"notif_analysis_complete_msg": "'{name}' wurde erfolgreich analysiert.",
166+
"notif_all_in_one_complete_title": "Auto-Deploy beendet",
167+
"notif_all_in_one_complete_msg": "Der Deployment-Flow für '{name}' wurde erfolgreich abgeschlossen.",
168+
"notif_winget_download_complete_title": "Download abgeschlossen",
169+
"notif_winget_download_complete_msg": "Der Installer für '{name}' wurde in deinen Downloads-Ordner heruntergeladen.",
170+
"notif_winget_install_complete_title": "Installation beendet",
171+
"notif_winget_install_complete_msg": "Die Installation von '{name}' wurde abgeschlossen.",
172+
"notif_intune_upload_complete_title": "Upload erfolgreich",
173+
"notif_intune_upload_complete_msg": "'{name}' wurde erfolgreich nach Intune hochgeladen.",
174+
"notif_stack_deploy_complete_title": "Stack bereitgestellt",
175+
"notif_stack_deploy_complete_msg": "Der Projekt-Stack '{name}' wurde erfolgreich bereitgestellt.",
176+
"notif_addon_install_complete_title": "Addon installiert",
177+
"notif_addon_install_complete_msg": "Das Addon '{name}' wurde erfolgreich installiert.",
178+
"notif_btn_open_folder": "Ordner öffnen",
179+
"notif_btn_open_intune": "Intune öffnen",
162180
"browse": "Durchsuchen",
163181
"search": "Suchen",
164182
"addon_missing_msg_ai": "Das KI-Addon ist nicht installiert oder konnte nicht geladen werden.",

src/switchcraft/assets/lang/en.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,24 @@
160160
"intune_log_label": "Output Log:",
161161
"intune_start_creation": "Starting package creation...",
162162
"intune_pkg_success": "Package created in:\n{path}",
163+
"notif_intunewin_complete_title": "IntuneWin Ready",
164+
"notif_intunewin_complete_msg": "The package '{name}' was created successfully.",
165+
"notif_analysis_complete_title": "Analysis Complete",
166+
"notif_analysis_complete_msg": "Successfully analyzed '{name}'.",
167+
"notif_all_in_one_complete_title": "Auto-Deploy Finished",
168+
"notif_all_in_one_complete_msg": "Deployment flow for '{name}' completed successfully.",
169+
"notif_winget_download_complete_title": "Download Complete",
170+
"notif_winget_download_complete_msg": "Installer for '{name}' downloaded to your Downloads folder.",
171+
"notif_winget_install_complete_title": "Installation Finished",
172+
"notif_winget_install_complete_msg": "The installation of '{name}' has completed.",
173+
"notif_intune_upload_complete_title": "Upload Successful",
174+
"notif_intune_upload_complete_msg": "'{name}' was successfully uploaded to Intune.",
175+
"notif_stack_deploy_complete_title": "Stack Deployed",
176+
"notif_stack_deploy_complete_msg": "Project stack '{name}' was deployed successfully.",
177+
"notif_addon_install_complete_title": "Addon Installed",
178+
"notif_addon_install_complete_msg": "The addon '{name}' was installed successfully.",
179+
"notif_btn_open_folder": "Open Folder",
180+
"notif_btn_open_intune": "Open Intune",
163181
"browse": "Browse",
164182
"search": "Search",
165183
"addon_missing_msg_ai": "The AI Addon is not installed or failed to load.",

src/switchcraft/gui_modern/app.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,14 +1909,28 @@ def _on_notification_update(self):
19091909
# Button 2: Open App
19101910
toast.add_actions(label=i18n.get("notif_open_app") or "Open App", launch="switchcraft://notifications")
19111911
else:
1912-
# Regular notifications (error/info/warning)
1913-
# Button 1: Open Logs Folder (if exists)
1914-
logs_path = Path(os.getenv('APPDATA', '')) / "FaserF" / "SwitchCraft" / "Logs"
1915-
if logs_path.exists():
1916-
toast.add_actions(label=i18n.get("notif_open_logs") or "Open Logs", launch="file://{logs_path}")
1917-
1918-
if notif_type == "error":
1919-
toast.add_actions(label=i18n.get("notif_open_app") or "Open App", launch="switchcraft://notifications")
1912+
# Regular notifications (error/info/warning/success)
1913+
1914+
# 1. Custom Path (e.g. Open Folder)
1915+
custom_path = n_data.get("path")
1916+
if custom_path and os.path.exists(custom_path):
1917+
toast.add_actions(label=i18n.get("notif_btn_open_folder") or "Open Folder", launch=f"file://{custom_path}")
1918+
1919+
# 2. Custom URL (e.g. Open Intune)
1920+
custom_url = n_data.get("url")
1921+
if custom_url:
1922+
label = i18n.get("notif_btn_open_intune") if "intune.microsoft.com" in custom_url else "Open Link"
1923+
toast.add_actions(label=label, launch=custom_url)
1924+
1925+
# 3. Default buttons for specific types
1926+
if not custom_path and not custom_url:
1927+
# Button 1: Open Logs Folder (if exists)
1928+
logs_path = Path(os.getenv('APPDATA', '')) / "FaserF" / "SwitchCraft" / "Logs"
1929+
if logs_path.exists():
1930+
toast.add_actions(label=i18n.get("notif_open_logs") or "Open Logs", launch=f"file://{logs_path}")
1931+
1932+
if notif_type == "error":
1933+
toast.add_actions(label=i18n.get("notif_open_app") or "Open App", launch="switchcraft://notifications")
19201934

19211935
if notif_type == "error":
19221936
toast.set_audio(audio.LoopingAlarm, loop=False)

src/switchcraft/gui_modern/utils/view_utils.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,15 @@ def _launch_url(self, url: str):
209209
try:
210210
# Flet's launch_url
211211
logger.info(f"Using page.launch_url for: {url}")
212-
page.launch_url(url)
212+
res = page.launch_url(url)
213+
214+
# Handle potential coroutine (e.g. in newer Flet versions or async context)
215+
if inspect.isawaitable(res):
216+
if hasattr(page, "run_task"):
217+
async def _wait(coro):
218+
await coro
219+
page.run_task(_wait, res)
220+
# if no run_task, we can't do much in a sync method, but at least we caught it
213221
return
214222
except Exception as e:
215223
logger.error(f"page.launch_url failed: {e}")

src/switchcraft/gui_modern/views/analyzer_view.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,16 @@ def _run():
466466
success = AddonService().install_addon(str(addon_zip))
467467
if success:
468468
self._show_snack("Addon installed! Please restart SwitchCraft.", "GREEN")
469+
# Trigger Desktop Notification
470+
try:
471+
NotificationService().add_notification(
472+
title=i18n.get("notif_addon_install_complete_title") or "Addon Installed",
473+
message=(i18n.get("notif_addon_install_complete_msg") or "The addon '{name}' was installed successfully.").format(name="Advanced Features"),
474+
type="success",
475+
notify_system=True
476+
)
477+
except Exception as n_ex:
478+
logger.warning(f"Failed to trigger Addon installation notification: {n_ex}")
469479
self.addon_warning.visible = False
470480
else:
471481
self._show_snack("Installation failed. Check logs.", "RED")
@@ -526,6 +536,19 @@ def on_progress(pct, msg, eta=None):
526536
self.app_page.switchcraft_app.set_progress(visible=False)
527537

528538
self._show_results(result)
539+
540+
# Trigger Desktop Notification
541+
try:
542+
name = Path(filepath).name
543+
NotificationService().add_notification(
544+
title=i18n.get("notif_analysis_complete_title") or "Analysis Complete",
545+
message=(i18n.get("notif_analysis_complete_msg") or "Successfully analyzed '{name}'.").format(name=name),
546+
type="success",
547+
notify_system=True,
548+
data={"path": str(Path(filepath).parent)}
549+
)
550+
except Exception as n_ex:
551+
logger.warning(f"Failed to trigger analysis notification: {n_ex}")
529552
except Exception as ex:
530553
logger.exception("Analysis failed")
531554
self.status_text.value = f"Error: {ex}"
@@ -937,7 +960,17 @@ def _bg():
937960
def prog_cb(p, m): log(f"Upload: {int(p * 100)}% - {m}")
938961
app_id = self.intune_service.upload_win32_app(token, pkg_path, app_meta, progress_callback=prog_cb)
939962
log(f"\nSUCCESS! App ID: {app_id}")
940-
NotificationService.send_notification("Intune Upload Success", f"Uploaded {info.product_name} to Intune.")
963+
# Trigger Desktop Notification
964+
try:
965+
NotificationService().add_notification(
966+
title=i18n.get("notif_all_in_one_complete_title") or "Auto-Deploy Finished",
967+
message=(i18n.get("notif_all_in_one_complete_msg") or "Deployment flow for '{name}' completed successfully.").format(name=info.product_name or "Application"),
968+
type="success",
969+
notify_system=True,
970+
data={"path": str(base_dir)}
971+
)
972+
except Exception as n_ex:
973+
logger.warning(f"Failed to trigger all-in-one notification: {n_ex}")
941974
self._add_history_entry(info, "Deployed")
942975
else:
943976
log("\nSkipping Intune Upload (No Tenant ID configured).")
@@ -1205,6 +1238,18 @@ def _bg():
12051238
try:
12061239
self.intune_service.create_intunewin(str(source), setup_file, str(output), quiet=True)
12071240

1241+
# Trigger Desktop Notification
1242+
try:
1243+
NotificationService().add_notification(
1244+
title=i18n.get("notif_intunewin_complete_title") or "IntuneWin Ready",
1245+
message=(i18n.get("notif_intunewin_complete_msg") or "The package '{name}' was created successfully.").format(name=setup_file),
1246+
type="success",
1247+
notify_system=True,
1248+
data={"path": str(output)}
1249+
)
1250+
except Exception as n_ex:
1251+
logger.warning(f"Failed to trigger IntuneWin creation notification: {n_ex}")
1252+
12081253
# Find the created file
12091254
expected_intunewin = source / (installer.stem + ".intunewin")
12101255
if expected_intunewin.exists():

src/switchcraft/gui_modern/views/intune_view.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pathlib import Path
66

77
from switchcraft.services.intune_service import IntuneService
8+
from switchcraft.services.notification_service import NotificationService
89
from switchcraft.gui_modern.utils.file_picker_helper import FilePickerHelper
910
from switchcraft.utils.config import SwitchCraftConfig
1011
from switchcraft.utils.i18n import i18n
@@ -644,6 +645,19 @@ def _bg():
644645
output_file = f
645646
break
646647

648+
# Trigger Desktop Notification
649+
try:
650+
pkg_name = os.path.basename(output_file)
651+
NotificationService().add_notification(
652+
title=i18n.get("notif_intunewin_complete_title") or "IntuneWin Ready",
653+
message=(i18n.get("notif_intunewin_complete_msg") or "The package '{name}' was created successfully.").format(name=pkg_name),
654+
type="success",
655+
notify_system=True,
656+
data={"path": str(output)}
657+
)
658+
except Exception as n_ex:
659+
logger.warning(f"Failed to trigger IntuneWin completion notification: {n_ex}")
660+
647661
def open_folder(e):
648662
import subprocess
649663
if os.name == 'nt':
@@ -732,6 +746,17 @@ def update_done():
732746
self.up_status.color = "GREEN"
733747
self.btn_upload.disabled = False
734748
self.update()
749+
# Trigger Desktop Notification
750+
try:
751+
NotificationService().add_notification(
752+
title=i18n.get("notif_intune_upload_complete_title") or "Upload Successful",
753+
message=(i18n.get("notif_intune_upload_complete_msg") or "'{name}' was successfully uploaded to Intune.").format(name=app_info.get("displayName") or "Application"),
754+
type="success",
755+
notify_system=True,
756+
data={"url": "https://intune.microsoft.com/#view/Microsoft_Intune_DeviceSettings/AppsWindowsAppsMenu"}
757+
)
758+
except Exception as n_ex:
759+
logger.warning(f"Failed to trigger Intune upload notification: {n_ex}")
735760
self._show_success_dialog(new_app_id)
736761
self._run_task_safe(update_done)
737762

src/switchcraft/gui_modern/views/sap_wizard_view.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def _build_admin_warning(self):
5555
ft.Text(i18n.get("sap_admin_required_title") or "Administrator Privileges Required", size=20, weight=ft.FontWeight.BOLD),
5656
ft.Text(i18n.get("sap_admin_required_desc") or "The SAP Installation Server Administration Tool (NwSapSetupAdmin.exe) requires administrative rights to merge updates and create packages."),
5757
ft.Container(height=20),
58-
ft.ElevatedButton(
58+
ft.FilledButton(
5959
i18n.get("btn_restart_admin") or "Restart SwitchCraft as Admin",
6060
icon=ft.Icons.SHIELD,
6161
on_click=lambda _: ShellUtils.restart_as_admin(),
@@ -95,7 +95,7 @@ def on_pick_server(e: ft.FilePickerResultEvent):
9595
ft.Text(i18n.get("sap_step1_title") or "1. Select SAP Installation Server", size=18, weight=ft.FontWeight.BOLD),
9696
ft.Text(i18n.get("sap_step1_desc") or "Point to the root folder of your SAP nwsetupadmin server."),
9797
ft.Row([
98-
ft.ElevatedButton(i18n.get("btn_browse_folder") or "Browse Server Folder", icon=ft.Icons.FOLDER_OPEN, on_click=lambda _: fp.get_directory_path()),
98+
ft.FilledButton(i18n.get("btn_browse_folder") or "Browse Server Folder", icon=ft.Icons.FOLDER_OPEN, on_click=lambda _: fp.get_directory_path()),
9999
path_text
100100
]),
101101
ft.Divider(height=20, color="TRANSPARENT"),
@@ -108,7 +108,7 @@ def _build_step_2(self):
108108
return ft.Column([
109109
ft.Text(i18n.get("sap_step2_title") or "2. Add Updates & Add-ons (Optional)", size=18, weight=ft.FontWeight.BOLD),
110110
ft.Text(i18n.get("sap_step2_desc") or "Select .exe files to merge into the installation server."),
111-
ft.ElevatedButton(i18n.get("btn_add_update") or "Add Update EXE", icon=ft.Icons.ADD, on_click=lambda _: self._show_snack("Not implemented in stub")),
111+
ft.FilledButton(i18n.get("btn_add_update") or "Add Update EXE", icon=ft.Icons.ADD, on_click=lambda _: self._show_snack("Not implemented in stub")),
112112
ft.ListView(expand=True, height=100) # Placeholder for file list
113113
])
114114

@@ -118,7 +118,7 @@ def _build_step_3(self):
118118
ft.Text(i18n.get("sap_step3_title") or "3. Customization", size=18, weight=ft.FontWeight.BOLD),
119119
ft.Checkbox(label=i18n.get("sap_use_webview2") or "Default to Edge WebView2 (Recommended)", value=self.use_webview2, on_change=lambda e: setattr(self, 'use_webview2', e.control.value)),
120120
ft.Row([
121-
ft.ElevatedButton(i18n.get("btn_select_logo") or "Select Custom Logo", icon=ft.Icons.IMAGE),
121+
ft.FilledButton(i18n.get("btn_select_logo") or "Select Custom Logo", icon=ft.Icons.IMAGE),
122122
ft.Text(i18n.get("no_logo_selected") or "No logo selected", italic=True)
123123
])
124124
])
@@ -152,13 +152,13 @@ def _build_step_4(self):
152152
ft.Divider(),
153153
self.package_dd,
154154
ft.Container(height=10),
155-
ft.ElevatedButton(i18n.get("btn_apply_build") or "Apply & Build Packaging", icon=ft.Icons.BUILD_CIRCLE, bgcolor="PRIMARY", color="WHITE", on_click=self._on_finalize)
155+
ft.FilledButton(i18n.get("btn_apply_build") or "Apply & Build Packaging", icon=ft.Icons.BUILD_CIRCLE, bgcolor="PRIMARY", color="WHITE", on_click=self._on_finalize)
156156
])
157157

158158
def _build_nav_buttons(self):
159159
return ft.Row([
160160
ft.TextButton(i18n.get("btn_back") or "Back", on_click=lambda _: self._show_step(self.current_step - 1) if self.current_step > 1 else None),
161-
ft.ElevatedButton(i18n.get("btn_next") or "Next", on_click=lambda _: self._show_step(self.current_step + 1) if self.current_step < 4 else None)
161+
ft.FilledButton(i18n.get("btn_next") or "Next", on_click=lambda _: self._show_step(self.current_step + 1) if self.current_step < 4 else None)
162162
], alignment=ft.MainAxisAlignment.END)
163163

164164
def _on_finalize(self, _):

src/switchcraft/gui_modern/views/stack_manager_view.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
from pathlib import Path
55
from switchcraft.utils.i18n import i18n
6+
from switchcraft.services.notification_service import NotificationService
67
from switchcraft.gui_modern.utils.view_utils import ViewMixin
78

89
logger = logging.getLogger(__name__)
@@ -350,6 +351,16 @@ def _deploy_worker():
350351
self._run_task_with_fallback(
351352
lambda: self._show_snack(f"Stack {self.current_stack} deployed successfully!", "GREEN")
352353
)
354+
# Trigger Desktop Notification
355+
try:
356+
NotificationService().add_notification(
357+
title=i18n.get("notif_stack_deploy_complete_title") or "Stack Deployed",
358+
message=(i18n.get("notif_stack_deploy_complete_msg") or "Project stack '{name}' was deployed successfully.").format(name=self.current_stack),
359+
type="success",
360+
notify_system=True
361+
)
362+
except Exception as n_ex:
363+
logger.warning(f"Failed to trigger stack deployment notification: {n_ex}")
353364
except Exception as e:
354365
logger.error(f"Stack deployment failed: {e}")
355366
self._run_task_with_fallback(

0 commit comments

Comments
 (0)