import React, { Dispatch, createContext, useContext, useReducer, useEffect } from "react"; import { Image } from "../entities"; import { useTitles } from "../titles"; export const defaultImage: Image = { title: "", author: "", url: "https://i.imgur.com/w4toMiR.jpg", }; export const defaultImages = (rows: number, cols: number) => { let images = localStorage.getItem("images"); if (images !== null) { return JSON.parse(images); } let imgs: Image[] = []; if (imgs.length !== 0) { return []; } for (let i = 0; i < rows * cols; i++) { imgs.push(defaultImage); } return imgs; }; type ImageSource = "collage" | "results"; export type ImageGrid = { images: Image[]; draggedImage: Image; draggedSource: ImageSource; draggedTarget: ImageSource; positionDragged: number; positionTarget: number; }; export const ImageGridDefault: ImageGrid = { images: defaultImages(10, 10), draggedImage: { ...defaultImage }, draggedSource: "collage", draggedTarget: "collage", positionDragged: 0, positionTarget: 0, }; type ImageGridAction = { type: "replace" | "exchange" | "update" | "reset"; field?: "draggedImage" | "draggedSource" | "draggedTarget" | "positionDragged" | "positionTarget"; value?: Image | ImageSource | number; }; const ImageGridReducer = (state: ImageGrid, action: ImageGridAction): ImageGrid => { switch (action.type) { case "update": return { ...state, [action.field as string]: action.value }; case "reset": localStorage.clear(); return { ...ImageGridDefault }; case "exchange": const images = [...state.images]; const { positionDragged, positionTarget } = state; const tmp = images[positionTarget]; images[positionTarget] = images[positionDragged]; images[positionDragged] = tmp; return { ...state, images }; case "replace": const imgs = [...state.images]; imgs[state.positionTarget] = state.draggedImage; return { ...state, images: imgs }; } }; const ImageGridContext = createContext<{ imageGrid: ImageGrid; dispatch: Dispatch<ImageGridAction>; }>({ imageGrid: { ...ImageGridDefault }, dispatch: () => null, }); export const useImageGrid = () => { const context = useContext(ImageGridContext); if (context === undefined) { throw new Error("useImages must be used within an Images provider"); } return context; }; export const ImageGridProvider: React.FC = ({ children }) => { const { titles, setTitles, lastUsed } = useTitles(); const [state, dispatch] = useReducer(ImageGridReducer, titles[lastUsed].imageGrid); useEffect(() => { const t = [...titles]; t[lastUsed].imageGrid = state; setTitles(t); // eslint-disable-next-line react-hooks/exhaustive-deps }, [state]); return <ImageGridContext.Provider value={{ imageGrid: state, dispatch }}>{children}</ImageGridContext.Provider>; };