mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 10:23:41 +00:00
Merge pull request #51 from jhbruhn/refactor/31-split-machine-store
feature: Refactor useMachineStore into focused stores
This commit is contained in:
commit
851d37c1d2
6 changed files with 368 additions and 252 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useShallow } from "zustand/react/shallow";
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { useMachineStore } from "./stores/useMachineStore";
|
import { useMachineCacheStore } from "./stores/useMachineCacheStore";
|
||||||
import { usePatternStore } from "./stores/usePatternStore";
|
import { usePatternStore } from "./stores/usePatternStore";
|
||||||
import { useUIStore } from "./stores/useUIStore";
|
import { useUIStore } from "./stores/useUIStore";
|
||||||
import { AppHeader } from "./components/AppHeader";
|
import { AppHeader } from "./components/AppHeader";
|
||||||
|
|
@ -23,8 +23,8 @@ function App() {
|
||||||
document.title = `Respira v${__APP_VERSION__}`;
|
document.title = `Respira v${__APP_VERSION__}`;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Machine store - for auto-loading cached pattern
|
// Machine cache store - for auto-loading cached pattern
|
||||||
const { resumedPattern, resumeFileName } = useMachineStore(
|
const { resumedPattern, resumeFileName } = useMachineCacheStore(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
resumedPattern: state.resumedPattern,
|
resumedPattern: state.resumedPattern,
|
||||||
resumeFileName: state.resumeFileName,
|
resumeFileName: state.resumeFileName,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { useState, useCallback } from "react";
|
import { useState, useCallback } from "react";
|
||||||
import { useShallow } from "zustand/react/shallow";
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { useMachineStore, usePatternUploaded } from "../stores/useMachineStore";
|
import { useMachineStore, usePatternUploaded } from "../stores/useMachineStore";
|
||||||
|
import { useMachineUploadStore } from "../stores/useMachineUploadStore";
|
||||||
|
import { useMachineCacheStore } from "../stores/useMachineCacheStore";
|
||||||
import { usePatternStore } from "../stores/usePatternStore";
|
import { usePatternStore } from "../stores/usePatternStore";
|
||||||
import { useUIStore } from "../stores/useUIStore";
|
import { useUIStore } from "../stores/useUIStore";
|
||||||
import {
|
import {
|
||||||
|
|
@ -40,25 +42,28 @@ import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function FileUpload() {
|
export function FileUpload() {
|
||||||
// Machine store
|
// Machine store
|
||||||
const {
|
const { isConnected, machineStatus, machineInfo } = useMachineStore(
|
||||||
isConnected,
|
|
||||||
machineStatus,
|
|
||||||
uploadProgress,
|
|
||||||
isUploading,
|
|
||||||
machineInfo,
|
|
||||||
resumeAvailable,
|
|
||||||
resumeFileName,
|
|
||||||
uploadPattern,
|
|
||||||
} = useMachineStore(
|
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
isConnected: state.isConnected,
|
isConnected: state.isConnected,
|
||||||
machineStatus: state.machineStatus,
|
machineStatus: state.machineStatus,
|
||||||
|
machineInfo: state.machineInfo,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Machine upload store
|
||||||
|
const { uploadProgress, isUploading, uploadPattern } = useMachineUploadStore(
|
||||||
|
useShallow((state) => ({
|
||||||
uploadProgress: state.uploadProgress,
|
uploadProgress: state.uploadProgress,
|
||||||
isUploading: state.isUploading,
|
isUploading: state.isUploading,
|
||||||
machineInfo: state.machineInfo,
|
uploadPattern: state.uploadPattern,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Machine cache store
|
||||||
|
const { resumeAvailable, resumeFileName } = useMachineCacheStore(
|
||||||
|
useShallow((state) => ({
|
||||||
resumeAvailable: state.resumeAvailable,
|
resumeAvailable: state.resumeAvailable,
|
||||||
resumeFileName: state.resumeFileName,
|
resumeFileName: state.resumeFileName,
|
||||||
uploadPattern: state.uploadPattern,
|
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
useMachineStore,
|
useMachineStore,
|
||||||
usePatternUploaded,
|
usePatternUploaded,
|
||||||
} from "../../stores/useMachineStore";
|
} from "../../stores/useMachineStore";
|
||||||
|
import { useMachineUploadStore } from "../../stores/useMachineUploadStore";
|
||||||
import { usePatternStore } from "../../stores/usePatternStore";
|
import { usePatternStore } from "../../stores/usePatternStore";
|
||||||
import { Stage, Layer } from "react-konva";
|
import { Stage, Layer } from "react-konva";
|
||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
|
|
@ -25,10 +26,16 @@ import { usePatternTransform } from "../../hooks/usePatternTransform";
|
||||||
|
|
||||||
export function PatternCanvas() {
|
export function PatternCanvas() {
|
||||||
// Machine store
|
// Machine store
|
||||||
const { sewingProgress, machineInfo, isUploading } = useMachineStore(
|
const { sewingProgress, machineInfo } = useMachineStore(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
sewingProgress: state.sewingProgress,
|
sewingProgress: state.sewingProgress,
|
||||||
machineInfo: state.machineInfo,
|
machineInfo: state.machineInfo,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Machine upload store
|
||||||
|
const { isUploading } = useMachineUploadStore(
|
||||||
|
useShallow((state) => ({
|
||||||
isUploading: state.isUploading,
|
isUploading: state.isUploading,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
194
src/stores/useMachineCacheStore.ts
Normal file
194
src/stores/useMachineCacheStore.ts
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
import type { PesPatternData } from "../formats/import/pesImporter";
|
||||||
|
import { uuidToString } from "../services/PatternCacheService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Machine Cache Store
|
||||||
|
*
|
||||||
|
* Manages pattern caching and resume functionality.
|
||||||
|
* Handles checking for cached patterns on the machine and loading them.
|
||||||
|
* Extracted from useMachineStore for better separation of concerns.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface MachineCacheState {
|
||||||
|
// Resume state
|
||||||
|
resumeAvailable: boolean;
|
||||||
|
resumeFileName: string | null;
|
||||||
|
resumedPattern: {
|
||||||
|
pesData: PesPatternData;
|
||||||
|
uploadedPesData?: PesPatternData;
|
||||||
|
patternOffset?: { x: number; y: number };
|
||||||
|
patternRotation?: number;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
checkResume: () => Promise<PesPatternData | null>;
|
||||||
|
loadCachedPattern: () => Promise<{
|
||||||
|
pesData: PesPatternData;
|
||||||
|
uploadedPesData?: PesPatternData;
|
||||||
|
patternOffset?: { x: number; y: number };
|
||||||
|
patternRotation?: number;
|
||||||
|
} | null>;
|
||||||
|
|
||||||
|
// Helper methods for inter-store communication
|
||||||
|
setResumeAvailable: (available: boolean, fileName: string | null) => void;
|
||||||
|
clearResumeState: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMachineCacheStore = create<MachineCacheState>((set, get) => ({
|
||||||
|
// Initial state
|
||||||
|
resumeAvailable: false,
|
||||||
|
resumeFileName: null,
|
||||||
|
resumedPattern: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for resumable pattern on the machine
|
||||||
|
* Queries the machine for its current pattern UUID and checks if we have it cached
|
||||||
|
*/
|
||||||
|
checkResume: async (): Promise<PesPatternData | null> => {
|
||||||
|
try {
|
||||||
|
// Import here to avoid circular dependency
|
||||||
|
const { useMachineStore } = await import("./useMachineStore");
|
||||||
|
const { service, storageService } = useMachineStore.getState();
|
||||||
|
|
||||||
|
console.log("[Resume] Checking for cached pattern...");
|
||||||
|
|
||||||
|
const machineUuid = await service.getPatternUUID();
|
||||||
|
console.log(
|
||||||
|
"[Resume] Machine UUID:",
|
||||||
|
machineUuid ? uuidToString(machineUuid) : "none",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!machineUuid) {
|
||||||
|
console.log("[Resume] No pattern loaded on machine");
|
||||||
|
set({ resumeAvailable: false, resumeFileName: null });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uuidStr = uuidToString(machineUuid);
|
||||||
|
const cached = await storageService.getPatternByUUID(uuidStr);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
console.log(
|
||||||
|
"[Resume] Pattern found in cache:",
|
||||||
|
cached.fileName,
|
||||||
|
"Offset:",
|
||||||
|
cached.patternOffset,
|
||||||
|
"Rotation:",
|
||||||
|
cached.patternRotation,
|
||||||
|
"Has uploaded data:",
|
||||||
|
!!cached.uploadedPesData,
|
||||||
|
);
|
||||||
|
console.log("[Resume] Auto-loading cached pattern...");
|
||||||
|
set({
|
||||||
|
resumeAvailable: true,
|
||||||
|
resumeFileName: cached.fileName,
|
||||||
|
resumedPattern: {
|
||||||
|
pesData: cached.pesData,
|
||||||
|
uploadedPesData: cached.uploadedPesData,
|
||||||
|
patternOffset: cached.patternOffset,
|
||||||
|
patternRotation: cached.patternRotation,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch pattern info from machine
|
||||||
|
try {
|
||||||
|
const info = await service.getPatternInfo();
|
||||||
|
useMachineStore.setState({ patternInfo: info });
|
||||||
|
console.log("[Resume] Pattern info loaded from machine");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[Resume] Failed to load pattern info:", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cached.pesData;
|
||||||
|
} else {
|
||||||
|
console.log("[Resume] Pattern on machine not found in cache");
|
||||||
|
set({ resumeAvailable: false, resumeFileName: null });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[Resume] Failed to check resume:", err);
|
||||||
|
set({ resumeAvailable: false, resumeFileName: null });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load cached pattern data
|
||||||
|
* Used when the user wants to restore a previously uploaded pattern
|
||||||
|
*/
|
||||||
|
loadCachedPattern: async (): Promise<{
|
||||||
|
pesData: PesPatternData;
|
||||||
|
uploadedPesData?: PesPatternData;
|
||||||
|
patternOffset?: { x: number; y: number };
|
||||||
|
patternRotation?: number;
|
||||||
|
} | null> => {
|
||||||
|
const { resumeAvailable } = get();
|
||||||
|
if (!resumeAvailable) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Import here to avoid circular dependency
|
||||||
|
const { useMachineStore } = await import("./useMachineStore");
|
||||||
|
const { service, storageService, refreshPatternInfo } =
|
||||||
|
useMachineStore.getState();
|
||||||
|
|
||||||
|
const machineUuid = await service.getPatternUUID();
|
||||||
|
if (!machineUuid) return null;
|
||||||
|
|
||||||
|
const uuidStr = uuidToString(machineUuid);
|
||||||
|
const cached = await storageService.getPatternByUUID(uuidStr);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
console.log(
|
||||||
|
"[Resume] Loading cached pattern:",
|
||||||
|
cached.fileName,
|
||||||
|
"Offset:",
|
||||||
|
cached.patternOffset,
|
||||||
|
"Rotation:",
|
||||||
|
cached.patternRotation,
|
||||||
|
"Has uploaded data:",
|
||||||
|
!!cached.uploadedPesData,
|
||||||
|
);
|
||||||
|
await refreshPatternInfo();
|
||||||
|
return {
|
||||||
|
pesData: cached.pesData,
|
||||||
|
uploadedPesData: cached.uploadedPesData,
|
||||||
|
patternOffset: cached.patternOffset,
|
||||||
|
patternRotation: cached.patternRotation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
"[Resume] Failed to load cached pattern:",
|
||||||
|
err instanceof Error ? err.message : "Unknown error",
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set resume availability
|
||||||
|
* Used by other stores to update resume state
|
||||||
|
*/
|
||||||
|
setResumeAvailable: (available: boolean, fileName: string | null) => {
|
||||||
|
set({
|
||||||
|
resumeAvailable: available,
|
||||||
|
resumeFileName: fileName,
|
||||||
|
...(available === false && { resumedPattern: null }),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear resume state
|
||||||
|
* Called when pattern is deleted from machine
|
||||||
|
*/
|
||||||
|
clearResumeState: () => {
|
||||||
|
set({
|
||||||
|
resumeAvailable: false,
|
||||||
|
resumeFileName: null,
|
||||||
|
resumedPattern: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
@ -13,7 +13,6 @@ import { SewingMachineError } from "../utils/errorCodeHelpers";
|
||||||
import { uuidToString } from "../services/PatternCacheService";
|
import { uuidToString } from "../services/PatternCacheService";
|
||||||
import { createStorageService } from "../platform";
|
import { createStorageService } from "../platform";
|
||||||
import type { IStorageService } from "../platform/interfaces/IStorageService";
|
import type { IStorageService } from "../platform/interfaces/IStorageService";
|
||||||
import type { PesPatternData } from "../formats/import/pesImporter";
|
|
||||||
import { usePatternStore } from "./usePatternStore";
|
import { usePatternStore } from "./usePatternStore";
|
||||||
|
|
||||||
interface MachineState {
|
interface MachineState {
|
||||||
|
|
@ -34,20 +33,6 @@ interface MachineState {
|
||||||
patternInfo: PatternInfo | null;
|
patternInfo: PatternInfo | null;
|
||||||
sewingProgress: SewingProgress | null;
|
sewingProgress: SewingProgress | null;
|
||||||
|
|
||||||
// Upload state
|
|
||||||
uploadProgress: number;
|
|
||||||
isUploading: boolean;
|
|
||||||
|
|
||||||
// Resume state
|
|
||||||
resumeAvailable: boolean;
|
|
||||||
resumeFileName: string | null;
|
|
||||||
resumedPattern: {
|
|
||||||
pesData: PesPatternData;
|
|
||||||
uploadedPesData?: PesPatternData;
|
|
||||||
patternOffset?: { x: number; y: number };
|
|
||||||
patternRotation?: number;
|
|
||||||
} | null;
|
|
||||||
|
|
||||||
// Error state
|
// Error state
|
||||||
error: string | null;
|
error: string | null;
|
||||||
isPairingError: boolean;
|
isPairingError: boolean;
|
||||||
|
|
@ -67,25 +52,10 @@ interface MachineState {
|
||||||
refreshPatternInfo: () => Promise<void>;
|
refreshPatternInfo: () => Promise<void>;
|
||||||
refreshProgress: () => Promise<void>;
|
refreshProgress: () => Promise<void>;
|
||||||
refreshServiceCount: () => Promise<void>;
|
refreshServiceCount: () => Promise<void>;
|
||||||
uploadPattern: (
|
|
||||||
penData: Uint8Array,
|
|
||||||
uploadedPesData: PesPatternData, // Pattern with rotation applied (for machine upload)
|
|
||||||
fileName: string,
|
|
||||||
patternOffset?: { x: number; y: number },
|
|
||||||
patternRotation?: number,
|
|
||||||
originalPesData?: PesPatternData, // Original unrotated pattern (for caching)
|
|
||||||
) => Promise<void>;
|
|
||||||
startMaskTrace: () => Promise<void>;
|
startMaskTrace: () => Promise<void>;
|
||||||
startSewing: () => Promise<void>;
|
startSewing: () => Promise<void>;
|
||||||
resumeSewing: () => Promise<void>;
|
resumeSewing: () => Promise<void>;
|
||||||
deletePattern: () => Promise<void>;
|
deletePattern: () => Promise<void>;
|
||||||
checkResume: () => Promise<PesPatternData | null>;
|
|
||||||
loadCachedPattern: () => Promise<{
|
|
||||||
pesData: PesPatternData;
|
|
||||||
uploadedPesData?: PesPatternData;
|
|
||||||
patternOffset?: { x: number; y: number };
|
|
||||||
patternRotation?: number;
|
|
||||||
} | null>;
|
|
||||||
|
|
||||||
// Internal methods
|
// Internal methods
|
||||||
_setupSubscriptions: () => void;
|
_setupSubscriptions: () => void;
|
||||||
|
|
@ -104,11 +74,6 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
machineError: SewingMachineError.None,
|
machineError: SewingMachineError.None,
|
||||||
patternInfo: null,
|
patternInfo: null,
|
||||||
sewingProgress: null,
|
sewingProgress: null,
|
||||||
uploadProgress: 0,
|
|
||||||
isUploading: false,
|
|
||||||
resumeAvailable: false,
|
|
||||||
resumeFileName: null,
|
|
||||||
resumedPattern: null,
|
|
||||||
error: null,
|
error: null,
|
||||||
isPairingError: false,
|
isPairingError: false,
|
||||||
isCommunicating: false,
|
isCommunicating: false,
|
||||||
|
|
@ -116,76 +81,10 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
pollIntervalId: null,
|
pollIntervalId: null,
|
||||||
serviceCountIntervalId: null,
|
serviceCountIntervalId: null,
|
||||||
|
|
||||||
// Check for resumable pattern
|
|
||||||
checkResume: async (): Promise<PesPatternData | null> => {
|
|
||||||
try {
|
|
||||||
const { service, storageService } = get();
|
|
||||||
console.log("[Resume] Checking for cached pattern...");
|
|
||||||
|
|
||||||
const machineUuid = await service.getPatternUUID();
|
|
||||||
console.log(
|
|
||||||
"[Resume] Machine UUID:",
|
|
||||||
machineUuid ? uuidToString(machineUuid) : "none",
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!machineUuid) {
|
|
||||||
console.log("[Resume] No pattern loaded on machine");
|
|
||||||
set({ resumeAvailable: false, resumeFileName: null });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uuidStr = uuidToString(machineUuid);
|
|
||||||
const cached = await storageService.getPatternByUUID(uuidStr);
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
console.log(
|
|
||||||
"[Resume] Pattern found in cache:",
|
|
||||||
cached.fileName,
|
|
||||||
"Offset:",
|
|
||||||
cached.patternOffset,
|
|
||||||
"Rotation:",
|
|
||||||
cached.patternRotation,
|
|
||||||
"Has uploaded data:",
|
|
||||||
!!cached.uploadedPesData,
|
|
||||||
);
|
|
||||||
console.log("[Resume] Auto-loading cached pattern...");
|
|
||||||
set({
|
|
||||||
resumeAvailable: true,
|
|
||||||
resumeFileName: cached.fileName,
|
|
||||||
resumedPattern: {
|
|
||||||
pesData: cached.pesData,
|
|
||||||
uploadedPesData: cached.uploadedPesData,
|
|
||||||
patternOffset: cached.patternOffset,
|
|
||||||
patternRotation: cached.patternRotation,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch pattern info from machine
|
|
||||||
try {
|
|
||||||
const info = await service.getPatternInfo();
|
|
||||||
set({ patternInfo: info });
|
|
||||||
console.log("[Resume] Pattern info loaded from machine");
|
|
||||||
} catch (err) {
|
|
||||||
console.error("[Resume] Failed to load pattern info:", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cached.pesData;
|
|
||||||
} else {
|
|
||||||
console.log("[Resume] Pattern on machine not found in cache");
|
|
||||||
set({ resumeAvailable: false, resumeFileName: null });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("[Resume] Failed to check resume:", err);
|
|
||||||
set({ resumeAvailable: false, resumeFileName: null });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Connect to machine
|
// Connect to machine
|
||||||
connect: async () => {
|
connect: async () => {
|
||||||
try {
|
try {
|
||||||
const { service, checkResume } = get();
|
const { service } = get();
|
||||||
set({ error: null, isPairingError: false });
|
set({ error: null, isPairingError: false });
|
||||||
|
|
||||||
await service.connect();
|
await service.connect();
|
||||||
|
|
@ -202,8 +101,9 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
machineError: state.error,
|
machineError: state.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check for resume possibility
|
// Check for resume possibility using cache store
|
||||||
await checkResume();
|
const { useMachineCacheStore } = await import("./useMachineCacheStore");
|
||||||
|
await useMachineCacheStore.getState().checkResume();
|
||||||
|
|
||||||
// Start polling
|
// Start polling
|
||||||
get()._startPolling();
|
get()._startPolling();
|
||||||
|
|
@ -311,85 +211,6 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Upload pattern to machine
|
|
||||||
uploadPattern: async (
|
|
||||||
penData: Uint8Array,
|
|
||||||
uploadedPesData: PesPatternData, // Pattern with rotation applied (for machine upload)
|
|
||||||
fileName: string,
|
|
||||||
patternOffset?: { x: number; y: number },
|
|
||||||
patternRotation?: number,
|
|
||||||
originalPesData?: PesPatternData, // Original unrotated pattern (for caching)
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
isConnected,
|
|
||||||
service,
|
|
||||||
storageService,
|
|
||||||
refreshStatus,
|
|
||||||
refreshPatternInfo,
|
|
||||||
} = get();
|
|
||||||
if (!isConnected) {
|
|
||||||
set({ error: "Not connected to machine" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
set({ error: null, uploadProgress: 0, isUploading: true });
|
|
||||||
|
|
||||||
// Upload to machine using the rotated bounds
|
|
||||||
const uuid = await service.uploadPattern(
|
|
||||||
penData,
|
|
||||||
(progress) => {
|
|
||||||
set({ uploadProgress: progress });
|
|
||||||
},
|
|
||||||
uploadedPesData.bounds,
|
|
||||||
patternOffset,
|
|
||||||
);
|
|
||||||
|
|
||||||
set({ uploadProgress: 100 });
|
|
||||||
|
|
||||||
// Cache the ORIGINAL unrotated pattern with rotation angle AND the uploaded data
|
|
||||||
// This allows us to restore the editable state correctly and ensures the exact
|
|
||||||
// uploaded data is used on resume (prevents inconsistencies from version updates)
|
|
||||||
const pesDataToCache = originalPesData || uploadedPesData;
|
|
||||||
const uuidStr = uuidToString(uuid);
|
|
||||||
storageService.savePattern(
|
|
||||||
uuidStr,
|
|
||||||
pesDataToCache,
|
|
||||||
fileName,
|
|
||||||
patternOffset,
|
|
||||||
patternRotation,
|
|
||||||
uploadedPesData, // Cache the exact uploaded data
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
"[Cache] Saved pattern:",
|
|
||||||
fileName,
|
|
||||||
"with UUID:",
|
|
||||||
uuidStr,
|
|
||||||
"Offset:",
|
|
||||||
patternOffset,
|
|
||||||
"Rotation:",
|
|
||||||
patternRotation,
|
|
||||||
"(cached original unrotated data + uploaded data)",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clear resume state since we just uploaded
|
|
||||||
set({
|
|
||||||
resumeAvailable: false,
|
|
||||||
resumeFileName: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh status and pattern info after upload
|
|
||||||
await refreshStatus();
|
|
||||||
await refreshPatternInfo();
|
|
||||||
} catch (err) {
|
|
||||||
set({
|
|
||||||
error: err instanceof Error ? err.message : "Failed to upload pattern",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
set({ isUploading: false });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Start mask trace
|
// Start mask trace
|
||||||
startMaskTrace: async () => {
|
startMaskTrace: async () => {
|
||||||
const { isConnected, service, refreshStatus } = get();
|
const { isConnected, service, refreshStatus } = get();
|
||||||
|
|
@ -465,15 +286,19 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
set({
|
set({
|
||||||
patternInfo: null,
|
patternInfo: null,
|
||||||
sewingProgress: null,
|
sewingProgress: null,
|
||||||
uploadProgress: 0,
|
|
||||||
resumeAvailable: false,
|
|
||||||
resumeFileName: null,
|
|
||||||
resumedPattern: null, // Clear this to prevent auto-reload
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear uploaded pattern data in pattern store
|
// Clear uploaded pattern data in pattern store
|
||||||
usePatternStore.getState().clearUploadedPattern();
|
usePatternStore.getState().clearUploadedPattern();
|
||||||
|
|
||||||
|
// Clear upload state in upload store
|
||||||
|
const { useMachineUploadStore } = await import("./useMachineUploadStore");
|
||||||
|
useMachineUploadStore.getState().reset();
|
||||||
|
|
||||||
|
// Clear resume state in cache store
|
||||||
|
const { useMachineCacheStore } = await import("./useMachineCacheStore");
|
||||||
|
useMachineCacheStore.getState().clearResumeState();
|
||||||
|
|
||||||
await refreshStatus();
|
await refreshStatus();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
set({
|
set({
|
||||||
|
|
@ -484,54 +309,6 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Load cached pattern
|
|
||||||
loadCachedPattern: async (): Promise<{
|
|
||||||
pesData: PesPatternData;
|
|
||||||
uploadedPesData?: PesPatternData;
|
|
||||||
patternOffset?: { x: number; y: number };
|
|
||||||
patternRotation?: number;
|
|
||||||
} | null> => {
|
|
||||||
const { resumeAvailable, service, storageService, refreshPatternInfo } =
|
|
||||||
get();
|
|
||||||
if (!resumeAvailable) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const machineUuid = await service.getPatternUUID();
|
|
||||||
if (!machineUuid) return null;
|
|
||||||
|
|
||||||
const uuidStr = uuidToString(machineUuid);
|
|
||||||
const cached = await storageService.getPatternByUUID(uuidStr);
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
console.log(
|
|
||||||
"[Resume] Loading cached pattern:",
|
|
||||||
cached.fileName,
|
|
||||||
"Offset:",
|
|
||||||
cached.patternOffset,
|
|
||||||
"Rotation:",
|
|
||||||
cached.patternRotation,
|
|
||||||
"Has uploaded data:",
|
|
||||||
!!cached.uploadedPesData,
|
|
||||||
);
|
|
||||||
await refreshPatternInfo();
|
|
||||||
return {
|
|
||||||
pesData: cached.pesData,
|
|
||||||
uploadedPesData: cached.uploadedPesData,
|
|
||||||
patternOffset: cached.patternOffset,
|
|
||||||
patternRotation: cached.patternRotation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (err) {
|
|
||||||
set({
|
|
||||||
error:
|
|
||||||
err instanceof Error ? err.message : "Failed to load cached pattern",
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Setup service subscriptions
|
// Setup service subscriptions
|
||||||
_setupSubscriptions: () => {
|
_setupSubscriptions: () => {
|
||||||
const { service } = get();
|
const { service } = get();
|
||||||
|
|
@ -603,7 +380,12 @@ export const useMachineStore = create<MachineState>((set, get) => ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// follows the apps logic:
|
// follows the apps logic:
|
||||||
if (get().resumeAvailable && get().patternInfo?.totalStitches == 0) {
|
// Check if we have a cached pattern and pattern info needs refreshing
|
||||||
|
const { useMachineCacheStore } = await import("./useMachineCacheStore");
|
||||||
|
if (
|
||||||
|
useMachineCacheStore.getState().resumeAvailable &&
|
||||||
|
get().patternInfo?.totalStitches == 0
|
||||||
|
) {
|
||||||
await refreshPatternInfo();
|
await refreshPatternInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
128
src/stores/useMachineUploadStore.ts
Normal file
128
src/stores/useMachineUploadStore.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
import type { PesPatternData } from "../formats/import/pesImporter";
|
||||||
|
import { uuidToString } from "../services/PatternCacheService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Machine Upload Store
|
||||||
|
*
|
||||||
|
* Manages the state and logic for uploading patterns to the machine.
|
||||||
|
* Extracted from useMachineStore for better separation of concerns.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface MachineUploadState {
|
||||||
|
// Upload state
|
||||||
|
uploadProgress: number;
|
||||||
|
isUploading: boolean;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
uploadPattern: (
|
||||||
|
penData: Uint8Array,
|
||||||
|
uploadedPesData: PesPatternData, // Pattern with rotation applied (for machine upload)
|
||||||
|
fileName: string,
|
||||||
|
patternOffset?: { x: number; y: number },
|
||||||
|
patternRotation?: number,
|
||||||
|
originalPesData?: PesPatternData, // Original unrotated pattern (for caching)
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
reset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMachineUploadStore = create<MachineUploadState>((set) => ({
|
||||||
|
// Initial state
|
||||||
|
uploadProgress: 0,
|
||||||
|
isUploading: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a pattern to the machine
|
||||||
|
*
|
||||||
|
* @param penData - The PEN-formatted pattern data to upload
|
||||||
|
* @param uploadedPesData - Pattern with rotation applied (for machine)
|
||||||
|
* @param fileName - Name of the pattern file
|
||||||
|
* @param patternOffset - Pattern position offset
|
||||||
|
* @param patternRotation - Rotation angle in degrees
|
||||||
|
* @param originalPesData - Original unrotated pattern (for caching)
|
||||||
|
*/
|
||||||
|
uploadPattern: async (
|
||||||
|
penData: Uint8Array,
|
||||||
|
uploadedPesData: PesPatternData,
|
||||||
|
fileName: string,
|
||||||
|
patternOffset?: { x: number; y: number },
|
||||||
|
patternRotation?: number,
|
||||||
|
originalPesData?: PesPatternData,
|
||||||
|
) => {
|
||||||
|
// Import here to avoid circular dependency
|
||||||
|
const { useMachineStore } = await import("./useMachineStore");
|
||||||
|
const {
|
||||||
|
isConnected,
|
||||||
|
service,
|
||||||
|
storageService,
|
||||||
|
refreshStatus,
|
||||||
|
refreshPatternInfo,
|
||||||
|
} = useMachineStore.getState();
|
||||||
|
|
||||||
|
if (!isConnected) {
|
||||||
|
throw new Error("Not connected to machine");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
set({ uploadProgress: 0, isUploading: true });
|
||||||
|
|
||||||
|
// Upload to machine using the rotated bounds
|
||||||
|
const uuid = await service.uploadPattern(
|
||||||
|
penData,
|
||||||
|
(progress) => {
|
||||||
|
set({ uploadProgress: progress });
|
||||||
|
},
|
||||||
|
uploadedPesData.bounds,
|
||||||
|
patternOffset,
|
||||||
|
);
|
||||||
|
|
||||||
|
set({ uploadProgress: 100 });
|
||||||
|
|
||||||
|
// Cache the ORIGINAL unrotated pattern with rotation angle AND the uploaded data
|
||||||
|
// This allows us to restore the editable state correctly and ensures the exact
|
||||||
|
// uploaded data is used on resume (prevents inconsistencies from version updates)
|
||||||
|
const pesDataToCache = originalPesData || uploadedPesData;
|
||||||
|
const uuidStr = uuidToString(uuid);
|
||||||
|
storageService.savePattern(
|
||||||
|
uuidStr,
|
||||||
|
pesDataToCache,
|
||||||
|
fileName,
|
||||||
|
patternOffset,
|
||||||
|
patternRotation,
|
||||||
|
uploadedPesData, // Cache the exact uploaded data
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"[MachineUpload] Saved pattern:",
|
||||||
|
fileName,
|
||||||
|
"with UUID:",
|
||||||
|
uuidStr,
|
||||||
|
"Offset:",
|
||||||
|
patternOffset,
|
||||||
|
"Rotation:",
|
||||||
|
patternRotation,
|
||||||
|
"(cached original unrotated data + uploaded data)",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear resume state in cache store since we just uploaded
|
||||||
|
const { useMachineCacheStore } = await import("./useMachineCacheStore");
|
||||||
|
useMachineCacheStore.getState().setResumeAvailable(false, null);
|
||||||
|
|
||||||
|
// Refresh status and pattern info after upload
|
||||||
|
await refreshStatus();
|
||||||
|
await refreshPatternInfo();
|
||||||
|
} catch (err) {
|
||||||
|
throw err instanceof Error ? err : new Error("Failed to upload pattern");
|
||||||
|
} finally {
|
||||||
|
set({ isUploading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset upload state
|
||||||
|
* Called when pattern is deleted from machine
|
||||||
|
*/
|
||||||
|
reset: () => {
|
||||||
|
set({ uploadProgress: 0, isUploading: false });
|
||||||
|
},
|
||||||
|
}));
|
||||||
Loading…
Reference in a new issue