Sfoglia il codice sorgente

Improve SpoolBuddy update UX: auto-check and remove beta toggle

  The SpoolBuddy layout now auto-checks for daemon updates every 5
  minutes and shows "Update available: v{version}" in the status bar.
  Removed the beta toggle since SpoolBuddy follows Bambuddy's release
  channel. The daemon version is now read from backend APP_VERSION
  instead of a stale hardcoded string.
maziggy 5 mesi fa
parent
commit
311a14e7ec

+ 1 - 1
CHANGELOG.md

@@ -5,7 +5,7 @@ All notable changes to Bambuddy will be documented in this file.
 ## [0.2.3b1] - Unreleased
 
 ### New Features
-- **SpoolBuddy OTA Updates** — SpoolBuddy devices can now be updated directly from the Settings → Updates tab without SSH access. Click "Check for Updates" to see if a newer version is available, then "Apply Update" to trigger the update. The daemon picks up the command via its heartbeat, pulls the latest code from GitHub, installs dependencies, and restarts automatically via systemd. Live progress is shown in the UI with status messages from the device. Requires the device to be online.
+- **SpoolBuddy OTA Updates** — SpoolBuddy devices can now be updated directly from the Settings → Updates tab without SSH access. Click "Check for Updates" to see if a newer version is available, then "Apply Update" to trigger the update. The daemon picks up the command via its heartbeat, pulls the latest code from GitHub, installs dependencies, and restarts automatically via systemd. Live progress is shown in the UI with status messages from the device. The status bar at the bottom automatically checks for updates every 5 minutes and shows a prominent message when one is available. Requires the device to be online.
 - **Select Plates to Queue** ([#777](https://github.com/maziggy/bambuddy/issues/777)) — Multi-plate 3MF files now support selecting a subset of plates to queue, instead of only "one plate" or "all plates". In add-to-queue mode, each plate has a checkbox for multi-select, with a "Select All / Deselect All" toggle. Reprint and edit modes remain single-select. Requested by @stringham.
 - **Camera Image Rotation** ([#672](https://github.com/maziggy/bambuddy/issues/672)) — Added per-printer camera rotation (0°, 90°, 180°, 270°) for cameras mounted in portrait or upside-down orientations. Configurable in Settings → Camera for each printer. Rotation applies to live stream, embedded viewer, stream overlay, and notification snapshots. Requested by @wrenoud.
 - **Per-User Email Notifications** ([#693](https://github.com/maziggy/bambuddy/pull/693)) — When Advanced Authentication is enabled, individual users can now receive email notifications for their own print jobs. A new "Notifications" page lets each user toggle notifications for print start, complete, failed, and stopped events. Only prints submitted by that user trigger their email — other users' prints are not affected. Requires SMTP to be configured and the "User Notifications" toggle enabled in Settings → Notifications. Administrators and Operators have access by default; Viewers do not. Contributed by @cadtoolbox.

+ 13 - 2
frontend/src/components/spoolbuddy/SpoolBuddyLayout.tsx

@@ -58,14 +58,25 @@ export function SpoolBuddyLayout() {
     };
   }, []);
 
-  // Update alert based on device state
+  // Auto-check for SpoolBuddy daemon updates
+  const { data: updateCheck } = useQuery({
+    queryKey: ['spoolbuddy-update-check', device?.device_id],
+    queryFn: () => device ? spoolbuddyApi.checkDaemonUpdate(device.device_id, true) : Promise.resolve(null),
+    enabled: !!device,
+    refetchInterval: 5 * 60 * 1000, // re-check every 5 minutes
+    staleTime: 4 * 60 * 1000,
+  });
+
+  // Update alert based on device state and available updates
   useEffect(() => {
     if (!sbState.deviceOnline) {
       setAlert({ type: 'warning', message: 'SpoolBuddy device disconnected' });
+    } else if (updateCheck?.update_available && updateCheck.latest_version) {
+      setAlert({ type: 'info', message: `Update available: v${updateCheck.latest_version}` });
     } else {
       setAlert(null);
     }
-  }, [sbState.deviceOnline]);
+  }, [sbState.deviceOnline, updateCheck]);
 
   // Track user activity for screen blank
   const resetActivity = useCallback(() => {

+ 7 - 51
frontend/src/pages/spoolbuddy/SpoolBuddySettingsPage.tsx

@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
 import { useOutletContext } from 'react-router-dom';
 import { useTranslation } from 'react-i18next';
 import type { SpoolBuddyOutletContext } from '../../components/spoolbuddy/SpoolBuddyLayout';
-import { spoolbuddyApi, type SpoolBuddyDevice, type DaemonUpdateCheck } from '../../api/client';
+import { spoolbuddyApi, type SpoolBuddyDevice } from '../../api/client';
 function formatUptime(seconds: number): string {
   if (seconds < 60) return `${seconds}s`;
   if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
@@ -502,45 +502,16 @@ function ScaleTab({ device, weight, weightStable, rawAdc }: {
 
 function UpdatesTab({ device }: { device: SpoolBuddyDevice }) {
   const { t } = useTranslation();
-  const [checking, setChecking] = useState(false);
   const [applying, setApplying] = useState(false);
-  const [updateResult, setUpdateResult] = useState<DaemonUpdateCheck | null>(null);
   const [error, setError] = useState<string | null>(null);
-  const [includeBeta, setIncludeBeta] = useState(() => {
-    try {
-      return localStorage.getItem('spoolbuddy-include-beta') === 'true';
-    } catch {
-      return false;
-    }
-  });
 
   const isUpdating = device.update_status === 'pending' || device.update_status === 'updating';
 
-  const toggleBeta = () => {
-    const next = !includeBeta;
-    setIncludeBeta(next);
-    try {
-      localStorage.setItem('spoolbuddy-include-beta', String(next));
-    } catch {
-      // localStorage unavailable
-    }
-    setUpdateResult(null);
-    setError(null);
-  };
-
-  const checkForUpdates = async () => {
-    setChecking(true);
-    setUpdateResult(null);
-    setError(null);
-    try {
-      const result = await spoolbuddyApi.checkDaemonUpdate(device.device_id, includeBeta);
-      setUpdateResult(result);
-    } catch (e) {
-      setError(e instanceof Error ? e.message : 'Failed to check for updates');
-    } finally {
-      setChecking(false);
-    }
-  };
+  const { data: updateResult, isLoading: checking, refetch } = useQuery({
+    queryKey: ['spoolbuddy-update-check', device.device_id],
+    queryFn: () => spoolbuddyApi.checkDaemonUpdate(device.device_id, true),
+    staleTime: 4 * 60 * 1000,
+  });
 
   const applyUpdate = async () => {
     setApplying(true);
@@ -554,7 +525,6 @@ function UpdatesTab({ device }: { device: SpoolBuddyDevice }) {
     }
   };
 
-  // Show version from device, or from update check result if available
   const displayVersion = device.firmware_version
     || (updateResult?.current_version && updateResult.current_version !== '0.0.0' ? updateResult.current_version : null);
 
@@ -617,7 +587,7 @@ function UpdatesTab({ device }: { device: SpoolBuddyDevice }) {
       {/* Check for updates */}
       <div className="bg-zinc-800 rounded-lg p-4 space-y-3">
         <button
-          onClick={checkForUpdates}
+          onClick={() => refetch()}
           disabled={checking || isUpdating}
           className="w-full px-4 py-2.5 rounded-lg text-sm font-medium bg-zinc-700 text-zinc-200 hover:bg-zinc-600 disabled:opacity-40 transition-colors min-h-[44px] flex items-center justify-center gap-2"
         >
@@ -681,20 +651,6 @@ function UpdatesTab({ device }: { device: SpoolBuddyDevice }) {
           </div>
         )}
 
-        {/* Include beta toggle */}
-        <div className="flex items-center justify-between pt-1">
-          <span className="text-xs text-zinc-500">{t('spoolbuddy.settings.includeBeta', 'Include beta versions')}</span>
-          <button
-            onClick={toggleBeta}
-            className={`relative w-10 h-5 rounded-full transition-colors ${
-              includeBeta ? 'bg-green-600' : 'bg-zinc-600'
-            }`}
-          >
-            <div className={`absolute top-0.5 w-4 h-4 bg-white rounded-full transition-transform ${
-              includeBeta ? 'translate-x-5' : 'translate-x-0.5'
-            }`} />
-          </button>
-        </div>
       </div>
     </div>
   );

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-BS4UxbHO.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-EtLQYE4Y.js"></script>
+    <script type="module" crossorigin src="/assets/index-BS4UxbHO.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CJ-drcFM.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff