mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-09 07:25:44 +02:00
fix(assign-spool): refresh printer status after assignment so card updates without manual Force-refresh (#1414 follow-up)
Reporter (@snozzlebert on A1 mini external slot) saw the Filament page Location column update correctly after Assign Spool, but the Printer card kept showing "Empty slot" until they manually pressed Force-refresh. MQTT command was going through fine; gap was client-side. AssignSpoolModal's two onSuccess callbacks invalidated the inventory / slot-assignment queries but never invalidated ['printerStatus', printerId] and never issued a pushall. For Bambu RFID-tagged spools the printer echoes the new tray_type on its own; for non-RFID spools and A1 mini external slots the firmware doesn't volunteer that state change. Added nudgePrinterRepublish() helper called from both onSuccess paths: api.refreshPrinterStatus(printerId) to issue the pushall (same call the Force-refresh button uses) plus invalidate printerStatus so the refetch lands. Refresh failures are swallowed — the assignment itself succeeded; a stale-cache nudge that didn't go through shouldn't surface as "assign failed". Same pattern as ConfigureAmsSlotModal since #1235, with the extra pushall because assign-spool affects firmware- side state.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -9,9 +9,12 @@ vi.mock('../../api/client', () => ({
|
||||
getSpools: vi.fn(),
|
||||
getAssignments: vi.fn(),
|
||||
assignSpool: vi.fn(),
|
||||
assignSpoolmanSlot: vi.fn(),
|
||||
getSpoolmanInventorySpools: vi.fn(),
|
||||
getSpoolmanSlotAssignments: vi.fn().mockResolvedValue([]),
|
||||
getSettings: vi.fn().mockResolvedValue({}),
|
||||
getAuthStatus: vi.fn().mockResolvedValue({ auth_enabled: false }),
|
||||
refreshPrinterStatus: vi.fn().mockResolvedValue({ status: 'ok' }),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -285,6 +288,41 @@ describe('AssignSpoolModal', () => {
|
||||
expect(screen.getByText(/Devil Design/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('nudges the printer to republish after successful assignment (#1414)', async () => {
|
||||
// The backend's assign-spool path issues an MQTT command, but firmware
|
||||
// (esp. A1 mini external slots and any non-RFID assignment) doesn't
|
||||
// always echo the new tray state back on its own — the printer card
|
||||
// then sits on stale data until the user hits Force-refresh. Modal
|
||||
// calls refreshPrinterStatus to issue a pushall so the printer
|
||||
// republishes state, mirroring the Force-refresh button.
|
||||
const { default: userEvent } = await import('@testing-library/user-event');
|
||||
const user = userEvent.setup();
|
||||
|
||||
// Tray material matches the spool to skip the mismatch confirm dialog.
|
||||
(api.getSpools as ReturnType<typeof vi.fn>).mockResolvedValue([manualSpool]);
|
||||
(api.assignSpool as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
id: 1, spool_id: 1, printer_id: 7, ams_id: 0, tray_id: 0,
|
||||
});
|
||||
|
||||
render(
|
||||
<AssignSpoolModal
|
||||
{...defaultProps}
|
||||
printerId={7}
|
||||
trayInfo={{ type: 'PLA', material: 'PLA', profile: 'PLA', color: 'FF0000', location: 'AMS 1 - Slot 1' }}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Polymaker/)).toBeInTheDocument();
|
||||
});
|
||||
await user.click(screen.getByText(/Polymaker/));
|
||||
await user.click(screen.getByRole('button', { name: /assign spool/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.refreshPrinterStatus).toHaveBeenCalledWith(7);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('AssignSpoolModal — Spoolman enabled (T-Gap 7)', () => {
|
||||
|
||||
@@ -113,6 +113,20 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
);
|
||||
}, [allSpoolmanAssignments, printerId, amsId, trayId]);
|
||||
|
||||
// #1414: nudge the printer to republish its state after we assign a
|
||||
// spool. The backend assign-spool path already issues an MQTT command,
|
||||
// but firmware (especially A1 mini external slots and any non-RFID
|
||||
// assignment) doesn't always echo the new tray state back on its own,
|
||||
// so the printer card sits on stale data and the user has to press
|
||||
// Force-refresh to see the assignment. Calling /refresh-status forces
|
||||
// a pushall the way the Force-refresh button does. Failures are
|
||||
// intentionally swallowed — the assignment itself succeeded; if the
|
||||
// refresh is offline the next poll / websocket update will catch up.
|
||||
const nudgePrinterRepublish = () => {
|
||||
api.refreshPrinterStatus(printerId).catch(() => {});
|
||||
queryClient.invalidateQueries({ queryKey: ['printerStatus', printerId] });
|
||||
};
|
||||
|
||||
const assignMutation = useMutation({
|
||||
mutationFn: (spoolId: number) =>
|
||||
api.assignSpool({ spool_id: spoolId, printer_id: printerId, ams_id: amsId, tray_id: trayId }),
|
||||
@@ -126,6 +140,7 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
return filtered;
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['spool-assignments'] });
|
||||
nudgePrinterRepublish();
|
||||
showToast(t('inventory.assignSuccess'), 'success');
|
||||
setShowMismatchConfirm(false);
|
||||
setPendingAssignId(null);
|
||||
@@ -148,6 +163,7 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['spoolman-inventory-spools'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['spoolman-slot-assignments'] });
|
||||
nudgePrinterRepublish();
|
||||
showToast(t('inventory.assignSuccess'), 'success');
|
||||
onClose();
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -26,7 +26,7 @@
|
||||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-CcGEQ2Qx.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-D9JqDSB2.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-KYwGxnG9.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user