From 712a55dfc70f21b97d2c1e3a55cfae0f06b02dce Mon Sep 17 00:00:00 2001 From: hugo Date: Mon, 16 Feb 2026 06:45:27 +0100 Subject: [PATCH] fix: phase 1 refactor --- REFACTOR_DUPLICATION.md | 6 +-- src/renderer/store/appStore.ts | 59 +++-------------------- tests/renderer/store/appStore.test.ts | 20 +++++++- tests/renderer/store/tabStore.test.ts | 2 + tests/renderer/utils/postGrouping.test.ts | 1 + 5 files changed, 31 insertions(+), 57 deletions(-) diff --git a/REFACTOR_DUPLICATION.md b/REFACTOR_DUPLICATION.md index b802d9a..bdb035b 100644 --- a/REFACTOR_DUPLICATION.md +++ b/REFACTOR_DUPLICATION.md @@ -7,7 +7,7 @@ Scope: Reduce high-impact code duplication in production and test-adjacent areas | Phase | Area | Status | Notes | |---|---|---|---| -| 1 | Electron API contract | ⚠️ Partial | Shared contract exists, but overlapping renderer store data interfaces remain. | +| 1 | Electron API contract | ✅ Completed | Shared contract now covers renderer store data interfaces too. | | 2 | Tag mutation workflow | ⚠️ Partial | Helpers exist; duplicate blocks still present in task flows. | | 3 | Post-media single/batch ops | ⚠️ Partial | Core primitives extracted; some duplicate link/unlink logic remains. | | 4 | Project mapping + delete guards | ✅ Completed | Guard + mapper extracted and used in target methods. | @@ -39,7 +39,7 @@ Scope: Reduce high-impact code duplication in production and test-adjacent areas ## Phase 1 — Consolidate Electron API Contract (Highest ROI) -Status: ⚠️ Partially complete +Status: ✅ Completed ### Problem `electronAPI` shape is duplicated across multiple files and can drift: @@ -59,7 +59,7 @@ Create one canonical API contract and consume it from both preload and renderer ### Progress Check - Completed: shared contract module exists and is imported by preload and renderer ambient types. -- Remaining: reduce overlapping data-shape duplication between shared API data interfaces and `src/renderer/store/appStore.ts` interfaces. +- Completed: overlapping data-shape duplication was removed by sourcing store data interfaces from shared contract types. ### Acceptance Criteria - Single source of truth for `electronAPI` contract shape. diff --git a/src/renderer/store/appStore.ts b/src/renderer/store/appStore.ts index d5723b1..558a402 100644 --- a/src/renderer/store/appStore.ts +++ b/src/renderer/store/appStore.ts @@ -1,6 +1,12 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import type { DeleteReference, ConfirmDeleteDetails } from '../components/ConfirmDeleteModal'; +import type { + ProjectData, + PostData, + MediaData, + TaskProgress, +} from '../../main/shared/electronApi'; // Storage key for persisted state const STORAGE_KEY = 'bds-app-state'; @@ -20,58 +26,7 @@ export interface TabState { } // Types -export interface ProjectData { - id: string; - name: string; - slug: string; - description?: string; - dataPath?: string; - isActive: boolean; - createdAt: string; - updatedAt: string; -} - -export interface PostData { - id: string; - title: string; - slug: string; - excerpt?: string; - content: string; - status: 'draft' | 'published' | 'archived'; - author?: string; - createdAt: string; - updatedAt: string; - publishedAt?: string; - tags: string[]; - categories: string[]; -} - -export interface MediaData { - id: string; - filename: string; - originalName: string; - mimeType: string; - size: number; - width?: number; - height?: number; - title?: string; - alt?: string; - caption?: string; - author?: string; - createdAt: string; - updatedAt: string; - tags: string[]; -} - -export interface TaskProgress { - taskId: string; - status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'; - progress: number; - message: string; - startTime: string; - endTime?: string; - error?: string; -} +export type { ProjectData, PostData, MediaData, TaskProgress }; export interface ErrorDetails { message: string; diff --git a/tests/renderer/store/appStore.test.ts b/tests/renderer/store/appStore.test.ts index 6f9a2ff..6fc7cd2 100644 --- a/tests/renderer/store/appStore.test.ts +++ b/tests/renderer/store/appStore.test.ts @@ -3,12 +3,19 @@ * Validates state management behavior for posts, dirty tracking, and UI state */ -import { describe, it, expect, beforeEach } from 'vitest'; -import { useAppStore, PostData } from '../../../src/renderer/store/appStore'; +import { describe, it, expect, expectTypeOf, beforeEach } from 'vitest'; +import { useAppStore, ProjectData, PostData, MediaData, TaskProgress } from '../../../src/renderer/store/appStore'; +import type { + ProjectData as SharedProjectData, + PostData as SharedPostData, + MediaData as SharedMediaData, + TaskProgress as SharedTaskProgress, +} from '../../../src/main/shared/electronApi'; // Helper to create a mock post const createMockPost = (overrides: Partial = {}): PostData => ({ id: `post-${Date.now()}-${Math.random().toString(36).substring(7)}`, + projectId: 'project-1', title: 'Test Post', slug: 'test-post', content: '# Test Content', @@ -160,4 +167,13 @@ describe('AppStore', () => { expect(getStore().preferredEditorMode).toBe('markdown'); }); }); + + describe('Type Contract', () => { + it('should keep store data types aligned with the shared Electron API contract', () => { + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + }); + }); }); diff --git a/tests/renderer/store/tabStore.test.ts b/tests/renderer/store/tabStore.test.ts index 128177c..2842da5 100644 --- a/tests/renderer/store/tabStore.test.ts +++ b/tests/renderer/store/tabStore.test.ts @@ -9,6 +9,7 @@ import { useAppStore, PostData, MediaData, Tab } from '../../../src/renderer/sto // Helper to create a mock post const createMockPost = (overrides: Partial = {}): PostData => ({ id: `post-${Date.now()}-${Math.random().toString(36).substring(7)}`, + projectId: 'project-1', title: 'Test Post', slug: 'test-post', content: '# Test Content', @@ -23,6 +24,7 @@ const createMockPost = (overrides: Partial = {}): PostData => ({ // Helper to create a mock media const createMockMedia = (overrides: Partial = {}): MediaData => ({ id: `media-${Date.now()}-${Math.random().toString(36).substring(7)}`, + projectId: 'project-1', filename: 'test.jpg', originalName: 'test.jpg', mimeType: 'image/jpeg', diff --git a/tests/renderer/utils/postGrouping.test.ts b/tests/renderer/utils/postGrouping.test.ts index 05441f1..b4ad53a 100644 --- a/tests/renderer/utils/postGrouping.test.ts +++ b/tests/renderer/utils/postGrouping.test.ts @@ -13,6 +13,7 @@ import type { PostData } from '../../../src/renderer/store/appStore'; function createTestPost(overrides: Partial): PostData { return { id: 'test-id', + projectId: 'project-1', title: 'Test Post', slug: 'test-post', excerpt: 'Test excerpt',