refactor: Derive patternUploaded from patternInfo instead of syncing state

Removed redundant patternUploaded state from PatternStore and replaced
it with a derived selector usePatternUploaded() in MachineStore that
computes it from patternInfo !== null.

This eliminates duplicate state, removes the need for synchronization
logic, and ensures a single source of truth for pattern upload status.

Updated all components (App, LeftSidebar, FileUpload, PatternCanvas,
WorkflowStepper) to use the derived selector.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Henrik Bruhn 2025-12-17 11:25:26 +01:00
parent c81930d1b7
commit 467eb9df95
5 changed files with 18 additions and 12 deletions

View file

@ -1,6 +1,6 @@
import { useState, useCallback } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { useMachineStore } from '../stores/useMachineStore';
import { useMachineStore, usePatternUploaded } from '../stores/useMachineStore';
import { usePatternStore } from '../stores/usePatternStore';
import { useUIStore } from '../stores/useUIStore';
import { convertPesToPen, type PesPatternData } from '../formats/import/pesImporter';
@ -40,18 +40,19 @@ export function FileUpload() {
pesData: pesDataProp,
currentFileName,
patternOffset,
patternUploaded,
setPattern,
} = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
currentFileName: state.currentFileName,
patternOffset: state.patternOffset,
patternUploaded: state.patternUploaded,
setPattern: state.setPattern,
}))
);
// Derived state: pattern is uploaded if machine has pattern info
const patternUploaded = usePatternUploaded();
// UI store
const {
pyodideReady,

View file

@ -1,5 +1,5 @@
import { useShallow } from 'zustand/react/shallow';
import { useMachineStore } from '../stores/useMachineStore';
import { useMachineStore, usePatternUploaded } from '../stores/useMachineStore';
import { usePatternStore } from '../stores/usePatternStore';
import { ConnectionPrompt } from './ConnectionPrompt';
import { FileUpload } from './FileUpload';
@ -13,13 +13,15 @@ export function LeftSidebar() {
}))
);
const { pesData, patternUploaded } = usePatternStore(
const { pesData } = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
patternUploaded: state.patternUploaded,
}))
);
// Derived state: pattern is uploaded if machine has pattern info
const patternUploaded = usePatternUploaded();
return (
<div className="flex flex-col gap-4 md:gap-5 lg:gap-6 lg:overflow-hidden">
{/* Connect Button or Browser Hint - Show when disconnected */}

View file

@ -1,6 +1,6 @@
import { useEffect, useRef, useState, useCallback } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { useMachineStore } from '../stores/useMachineStore';
import { useMachineStore, usePatternUploaded } from '../stores/useMachineStore';
import { usePatternStore } from '../stores/usePatternStore';
import { Stage, Layer, Group } from 'react-konva';
import Konva from 'konva';
@ -27,16 +27,17 @@ export function PatternCanvas() {
const {
pesData,
patternOffset: initialPatternOffset,
patternUploaded,
setPatternOffset,
} = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
patternOffset: state.patternOffset,
patternUploaded: state.patternUploaded,
setPatternOffset: state.setPatternOffset,
}))
);
// Derived state: pattern is uploaded if machine has pattern info
const patternUploaded = usePatternUploaded();
const containerRef = useRef<HTMLDivElement>(null);
const stageRef = useRef<Konva.Stage | null>(null);

View file

@ -1,6 +1,6 @@
import { useState, useRef, useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { useMachineStore } from '../stores/useMachineStore';
import { useMachineStore, usePatternUploaded } from '../stores/useMachineStore';
import { usePatternStore } from '../stores/usePatternStore';
import { CheckCircleIcon, InformationCircleIcon, ExclamationTriangleIcon } from '@heroicons/react/24/solid';
import { MachineStatus } from '../types/machine';
@ -268,14 +268,14 @@ export function WorkflowStepper() {
// Pattern store
const {
pesData,
patternUploaded,
} = usePatternStore(
useShallow((state) => ({
pesData: state.pesData,
patternUploaded: state.patternUploaded,
}))
);
// Derived state: pattern is uploaded if machine has pattern info
const patternUploaded = usePatternUploaded();
const hasPattern = pesData !== null;
const hasErrorFlag = hasError(machineError);
const currentStep = getCurrentStep(machineStatus, isConnected, hasPattern, patternUploaded);

View file

@ -552,3 +552,5 @@ export const useMachineStatus = () => useMachineStore((state) => state.machineSt
export const useMachineError = () => useMachineStore((state) => state.machineError);
export const usePatternInfo = () => useMachineStore((state) => state.patternInfo);
export const useSewingProgress = () => useMachineStore((state) => state.sewingProgress);
// Derived state: pattern is uploaded if machine has pattern info
export const usePatternUploaded = () => useMachineStore((state) => state.patternInfo !== null);