Просмотр исходного кода

Add swipe-to-switch-printer and auto-navigate on tag scan

  Swiping left/right on the SpoolBuddy touchscreen now cycles through
  online printers instead of triggering browser navigation. Auto-navigates
  to dashboard when a new NFC tag is detected on any other page, with
  edge detection so a persistent tag doesn't block manual navigation.
maziggy 2 месяцев назад
Родитель
Сommit
ceb599ec00

+ 1 - 0
CHANGELOG.md

@@ -20,6 +20,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **SpoolBuddy Assign-to-AMS Material Mismatch Warnings** — The SpoolBuddy "Assign to AMS" modal now warns when the spool's material or slicer profile doesn't match the target slot's current filament. Shows a confirmation dialog with five warning levels: exact material mismatch, partial material match, profile-only mismatch, and combined material+profile mismatches. Respects the global `disable_filament_warnings` setting. Previously, assigning a spool to an occupied slot proceeded without any validation, matching the behavior already present in the main Assign Spool modal.
 - **SpoolBuddy Assign-to-AMS Material Mismatch Warnings** — The SpoolBuddy "Assign to AMS" modal now warns when the spool's material or slicer profile doesn't match the target slot's current filament. Shows a confirmation dialog with five warning levels: exact material mismatch, partial material match, profile-only mismatch, and combined material+profile mismatches. Respects the global `disable_filament_warnings` setting. Previously, assigning a spool to an occupied slot proceeded without any validation, matching the behavior already present in the main Assign Spool modal.
 - **Spool Assignment Changes Sync Across Tabs** — Assigning or unassigning a spool now broadcasts a WebSocket event to all connected clients. Other open browser tabs and the SpoolBuddy frontend update automatically without requiring a page reload.
 - **Spool Assignment Changes Sync Across Tabs** — Assigning or unassigning a spool now broadcasts a WebSocket event to all connected clients. Other open browser tabs and the SpoolBuddy frontend update automatically without requiring a page reload.
 - **SpoolBuddy Auto-Navigate on Tag Scan** — When an NFC tag is detected while the SpoolBuddy UI is on a non-dashboard page (Settings, AMS, Write Tag, etc.), the frontend automatically navigates back to the main dashboard to show the scanned spool. Also wakes the screen if the display was blanked.
 - **SpoolBuddy Auto-Navigate on Tag Scan** — When an NFC tag is detected while the SpoolBuddy UI is on a non-dashboard page (Settings, AMS, Write Tag, etc.), the frontend automatically navigates back to the main dashboard to show the scanned spool. Also wakes the screen if the display was blanked.
+- **SpoolBuddy Swipe to Switch Printers** — Swiping left/right on the SpoolBuddy touchscreen now cycles through online printers instead of triggering browser back/forward navigation. The selected printer updates in the top bar dropdown. Requires at least two online printers; single-printer setups are unaffected.
 - **SpoolBuddy System Tab** — Added a "System" tab to SpoolBuddy Settings showing live OS stats from the Raspberry Pi: CPU temperature, core count, load average, memory usage, disk usage, OS distro/kernel/architecture, Python version, and system uptime. Stats are collected by the daemon every heartbeat (10s) using stdlib-only reads from `/proc` and `/sys` — no additional dependencies required. Usage bars turn amber at 70% and red at 90%; CPU temperature is color-coded green/amber/red.
 - **SpoolBuddy System Tab** — Added a "System" tab to SpoolBuddy Settings showing live OS stats from the Raspberry Pi: CPU temperature, core count, load average, memory usage, disk usage, OS distro/kernel/architecture, Python version, and system uptime. Stats are collected by the daemon every heartbeat (10s) using stdlib-only reads from `/proc` and `/sys` — no additional dependencies required. Usage bars turn amber at 70% and red at 90%; CPU temperature is color-coded green/amber/red.
 
 
 ### Fixed
 ### Fixed

+ 40 - 3
frontend/src/components/spoolbuddy/SpoolBuddyLayout.tsx

@@ -1,12 +1,12 @@
 import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
 import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
 import { Outlet, useNavigate, useLocation } from 'react-router-dom';
 import { Outlet, useNavigate, useLocation } from 'react-router-dom';
-import { useQuery } from '@tanstack/react-query';
+import { useQuery, useQueries } from '@tanstack/react-query';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { SpoolBuddyTopBar } from './SpoolBuddyTopBar';
 import { SpoolBuddyTopBar } from './SpoolBuddyTopBar';
 import { SpoolBuddyBottomNav } from './SpoolBuddyBottomNav';
 import { SpoolBuddyBottomNav } from './SpoolBuddyBottomNav';
 import { SpoolBuddyStatusBar } from './SpoolBuddyStatusBar';
 import { SpoolBuddyStatusBar } from './SpoolBuddyStatusBar';
 import { useSpoolBuddyState } from '../../hooks/useSpoolBuddyState';
 import { useSpoolBuddyState } from '../../hooks/useSpoolBuddyState';
-import { api, spoolbuddyApi } from '../../api/client';
+import { api, spoolbuddyApi, type Printer } from '../../api/client';
 import { VirtualKeyboard } from '../VirtualKeyboard';
 import { VirtualKeyboard } from '../VirtualKeyboard';
 
 
 export function SpoolBuddyLayout() {
 export function SpoolBuddyLayout() {
@@ -124,6 +124,41 @@ export function SpoolBuddyLayout() {
     return () => clearInterval(interval);
     return () => clearInterval(interval);
   }, [displayBlankTimeout]);
   }, [displayBlankTimeout]);
 
 
+  // Online printers list for swipe-to-switch
+  const { data: printers = [] } = useQuery({
+    queryKey: ['printers'],
+    queryFn: () => api.getPrinters(),
+  });
+  const statusQueries = useQueries({
+    queries: printers.map((printer: Printer) => ({
+      queryKey: ['printerStatus', printer.id],
+      queryFn: () => api.getPrinterStatus(printer.id),
+      refetchInterval: 10000,
+    })),
+  });
+  const onlinePrinters = useMemo(() => {
+    return printers.filter((_: Printer, i: number) => statusQueries[i]?.data?.connected);
+  }, [printers, statusQueries]);
+
+  // Swipe left/right to cycle through online printers
+  const touchStartRef = useRef<{ x: number; y: number } | null>(null);
+  const SWIPE_THRESHOLD = 50;
+  const handleTouchStart = useCallback((e: React.TouchEvent) => {
+    touchStartRef.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
+  }, []);
+  const handleTouchEnd = useCallback((e: React.TouchEvent) => {
+    if (!touchStartRef.current || onlinePrinters.length < 2) return;
+    const dx = e.changedTouches[0].clientX - touchStartRef.current.x;
+    const dy = e.changedTouches[0].clientY - touchStartRef.current.y;
+    touchStartRef.current = null;
+    if (Math.abs(dx) < SWIPE_THRESHOLD || Math.abs(dy) > Math.abs(dx)) return;
+    const currentIdx = onlinePrinters.findIndex((p: Printer) => p.id === selectedPrinterId);
+    const nextIdx = dx < 0
+      ? (currentIdx + 1) % onlinePrinters.length          // swipe left → next
+      : (currentIdx - 1 + onlinePrinters.length) % onlinePrinters.length; // swipe right → prev
+    setSelectedPrinterId(onlinePrinters[nextIdx].id);
+  }, [onlinePrinters, selectedPrinterId, setSelectedPrinterId]);
+
   // CSS brightness filter (software dimming)
   // CSS brightness filter (software dimming)
   const brightnessStyle = displayBrightness < 100
   const brightnessStyle = displayBrightness < 100
     ? { filter: `brightness(${displayBrightness / 100})` } as const
     ? { filter: `brightness(${displayBrightness / 100})` } as const
@@ -133,7 +168,9 @@ export function SpoolBuddyLayout() {
     <>
     <>
       <div
       <div
         className="w-screen h-screen bg-bambu-dark text-white flex flex-col overflow-hidden"
         className="w-screen h-screen bg-bambu-dark text-white flex flex-col overflow-hidden"
-        style={brightnessStyle}
+        style={{ ...brightnessStyle, overscrollBehaviorX: 'none' }}
+        onTouchStart={handleTouchStart}
+        onTouchEnd={handleTouchEnd}
       >
       >
         <SpoolBuddyTopBar
         <SpoolBuddyTopBar
           selectedPrinterId={selectedPrinterId}
           selectedPrinterId={selectedPrinterId}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-DRDn9t3d.js


+ 1 - 1
static/index.html

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

Некоторые файлы не были показаны из-за большого количества измененных файлов