snapsteps/src/features/guides/hooks/useNewGuide.ts
johannes-hernehult 7cdeb98161 feat(guides): integrate GuideContext and persist new guides to IndexedDB
- Wrap application root with IDBProvider and GuideProvider
    - Replace mock data in App component with real guides from useGuides hook
    - Update useNewGuide hook to save compiled guides to IndexedDB and update global state
2026-06-17 11:12:25 +02:00

159 lines
3.9 KiB
TypeScript

import { useState } from "react";
import {
type CompiledGuideType,
type FrameType,
type GuideType,
} from "../../../lib/schemas";
import { useIDB } from "../../../lib/contexts/dbinit.context";
import { useGuides } from "../../../lib/contexts/guide.context";
export default function useNewGuide() {
const { db } = useIDB();
const { setGuides } = useGuides();
const [guide, setGuide] = useState<GuideType>({
id: crypto.randomUUID(),
title: "",
description: "",
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: undefined,
});
const [frames, setFrames] = useState<FrameType[]>([]);
const handleUpdateTitle = (title: string) => {
setGuide((prev) => ({ ...prev, title }));
};
const handleUpdateDescription = (description: string) => {
setGuide((prev) => ({ ...prev, description }));
};
const handleAddFrames = (e: React.ChangeEvent<HTMLInputElement>) => {
const newFiles = e.target.files;
if (!newFiles) return;
const newFilesArray = Array.from(newFiles);
const newFrameObjects = newFilesArray.map((file) => {
const frameId = crypto.randomUUID();
return {
id: frameId,
file: file,
title: "",
guideId: guide.id,
steps: [{ id: crypto.randomUUID(), text: "", frameId: frameId }],
};
});
setFrames((prev) => [...prev, ...newFrameObjects]);
};
const handleUpdateFrameTitle = (frameId: string, title: string) => {
setFrames((prev) =>
prev.map((frame) => (frame.id === frameId ? { ...frame, title } : frame)),
);
};
const handleRemoveFrame = (frameId: string) => {
setFrames((prev) => prev.filter((frame) => frame.id !== frameId));
};
const handleReOrderFrames = (startIndex: number, endIndex: number) => {
setFrames((prev) => {
const newOrder = [...prev];
const [movedFrame] = newOrder.splice(startIndex, 1);
newOrder.splice(endIndex, 0, movedFrame);
return newOrder;
});
};
const handleAddSteps = (frameId: string) => {
setFrames((prev) =>
prev.map((frame) =>
frame.id === frameId
? {
...frame,
steps: [
...frame.steps,
{ id: crypto.randomUUID(), text: "", frameId: frameId },
],
}
: frame,
),
);
};
const handleUpdateStep = (stepId: string, text: string) => {
setFrames((prev) =>
prev.map((frame) =>
frame.steps
? {
...frame,
steps: frame.steps.map((step) =>
step.id === stepId ? { ...step, text } : step,
),
}
: frame,
),
);
};
const handleRemoveStep = (stepId: string) => {
setFrames((prev) =>
prev.map((frame) =>
frame.steps
? {
...frame,
steps: frame.steps.filter((step) => step.id !== stepId),
}
: frame,
),
);
};
const handleSaveGuide = async (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
const compiledGuide: CompiledGuideType = {
id: guide.id,
title: guide.title,
description: guide.description,
createdAt: guide.createdAt,
updatedAt: guide.updatedAt,
deletedAt: guide.deletedAt,
frames,
};
if (!db) return;
await db.put("guides", compiledGuide);
setGuide({
id: crypto.randomUUID(),
title: "",
description: "",
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: undefined,
});
setFrames([]);
setGuides((prev) => [compiledGuide, ...prev]);
};
return {
guide,
setGuide,
frames,
setFrames,
handleUpdateTitle,
handleUpdateDescription,
handleAddFrames,
handleUpdateFrameTitle,
handleReOrderFrames,
handleAddSteps,
handleRemoveFrame,
handleUpdateStep,
handleRemoveStep,
handleSaveGuide,
};
}