238 lines
6.6 KiB
TypeScript
238 lines
6.6 KiB
TypeScript
import { useCallback } from "react";
|
|
|
|
import type { BoardItemAdvancedOptions } from "@homarr/validation";
|
|
|
|
import type { Item } from "~/app/[locale]/boards/_types";
|
|
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
|
|
import type { CreateItemInput } from "./actions/create-item";
|
|
import { createItemCallback } from "./actions/create-item";
|
|
import type { DuplicateItemInput } from "./actions/duplicate-item";
|
|
import { duplicateItemCallback } from "./actions/duplicate-item";
|
|
|
|
interface MoveAndResizeItem {
|
|
itemId: string;
|
|
xOffset: number;
|
|
yOffset: number;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
interface MoveItemToSection {
|
|
itemId: string;
|
|
sectionId: string;
|
|
xOffset: number;
|
|
yOffset: number;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
interface RemoveItem {
|
|
itemId: string;
|
|
}
|
|
|
|
interface UpdateItemOptions {
|
|
itemId: string;
|
|
newOptions: Record<string, unknown>;
|
|
}
|
|
|
|
interface UpdateItemAdvancedOptions {
|
|
itemId: string;
|
|
newAdvancedOptions: BoardItemAdvancedOptions;
|
|
}
|
|
|
|
interface UpdateItemIntegrations {
|
|
itemId: string;
|
|
newIntegrations: string[];
|
|
}
|
|
|
|
export const useItemActions = () => {
|
|
const { updateBoard } = useUpdateBoard();
|
|
|
|
const createItem = useCallback(
|
|
(input: CreateItemInput) => {
|
|
updateBoard(createItemCallback(input));
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const duplicateItem = useCallback(
|
|
({ itemId }: DuplicateItemInput) => {
|
|
updateBoard(duplicateItemCallback({ itemId }));
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const updateItemOptions = useCallback(
|
|
({ itemId, newOptions }: UpdateItemOptions) => {
|
|
updateBoard((previous) => {
|
|
return {
|
|
...previous,
|
|
sections: previous.sections.map((section) => {
|
|
// Return same section if item is not in it
|
|
if (!section.items.some((item) => item.id === itemId)) return section;
|
|
return {
|
|
...section,
|
|
items: section.items.map((item) => {
|
|
// Return same item if item is not the one we're changing
|
|
if (item.id !== itemId) return item;
|
|
return {
|
|
...item,
|
|
options: newOptions,
|
|
};
|
|
}),
|
|
};
|
|
}),
|
|
};
|
|
});
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const updateItemAdvancedOptions = useCallback(
|
|
({ itemId, newAdvancedOptions }: UpdateItemAdvancedOptions) => {
|
|
updateBoard((previous) => {
|
|
return {
|
|
...previous,
|
|
sections: previous.sections.map((section) => {
|
|
// Return same section if item is not in it
|
|
if (!section.items.some((item) => item.id === itemId)) return section;
|
|
return {
|
|
...section,
|
|
items: section.items.map((item) => {
|
|
// Return same item if item is not the one we're changing
|
|
if (item.id !== itemId) return item;
|
|
return {
|
|
...item,
|
|
advancedOptions: newAdvancedOptions,
|
|
};
|
|
}),
|
|
};
|
|
}),
|
|
};
|
|
});
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const updateItemIntegrations = useCallback(
|
|
({ itemId, newIntegrations }: UpdateItemIntegrations) => {
|
|
updateBoard((previous) => {
|
|
return {
|
|
...previous,
|
|
sections: previous.sections.map((section) => {
|
|
// Return same section if item is not in it
|
|
if (!section.items.some((item) => item.id === itemId)) return section;
|
|
return {
|
|
...section,
|
|
items: section.items.map((item) => {
|
|
// Return same item if item is not the one we're moving
|
|
if (item.id !== itemId) return item;
|
|
return {
|
|
...item,
|
|
...("integrationIds" in item ? { integrationIds: newIntegrations } : {}),
|
|
};
|
|
}),
|
|
};
|
|
}),
|
|
};
|
|
});
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const moveAndResizeItem = useCallback(
|
|
({ itemId, ...positionProps }: MoveAndResizeItem) => {
|
|
updateBoard((previous) => ({
|
|
...previous,
|
|
sections: previous.sections.map((section) => {
|
|
// Return same section if item is not in it
|
|
if (!section.items.some((item) => item.id === itemId)) return section;
|
|
return {
|
|
...section,
|
|
items: section.items.map((item) => {
|
|
// Return same item if item is not the one we're moving
|
|
if (item.id !== itemId) return item;
|
|
return {
|
|
...item,
|
|
...positionProps,
|
|
} satisfies Item;
|
|
}),
|
|
};
|
|
}),
|
|
}));
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const moveItemToSection = useCallback(
|
|
({ itemId, sectionId, ...positionProps }: MoveItemToSection) => {
|
|
updateBoard((previous) => {
|
|
const currentSection = previous.sections.find((section) => section.items.some((item) => item.id === itemId));
|
|
|
|
// If item is in the same section (on initial loading) don't do anything
|
|
if (!currentSection) {
|
|
return previous;
|
|
}
|
|
|
|
const currentItem = currentSection.items.find((item) => item.id === itemId);
|
|
if (!currentItem) {
|
|
return previous;
|
|
}
|
|
|
|
if (currentSection.id === sectionId && currentItem.xOffset) {
|
|
return previous;
|
|
}
|
|
|
|
return {
|
|
...previous,
|
|
sections: previous.sections.map((section) => {
|
|
// Return sections without item if not section where it is moved to
|
|
if (section.id !== sectionId)
|
|
return {
|
|
...section,
|
|
items: section.items.filter((item) => item.id !== itemId),
|
|
};
|
|
|
|
// Return section and add item to it
|
|
return {
|
|
...section,
|
|
items: section.items
|
|
.filter((item) => item.id !== itemId)
|
|
.concat({
|
|
...currentItem,
|
|
...positionProps,
|
|
}),
|
|
};
|
|
}),
|
|
};
|
|
});
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
const removeItem = useCallback(
|
|
({ itemId }: RemoveItem) => {
|
|
updateBoard((previous) => {
|
|
return {
|
|
...previous,
|
|
// Filter removed item out of items array
|
|
sections: previous.sections.map((section) => ({
|
|
...section,
|
|
items: section.items.filter((item) => item.id !== itemId),
|
|
})),
|
|
};
|
|
});
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
return {
|
|
moveAndResizeItem,
|
|
moveItemToSection,
|
|
removeItem,
|
|
updateItemOptions,
|
|
updateItemAdvancedOptions,
|
|
updateItemIntegrations,
|
|
duplicateItem,
|
|
createItem,
|
|
};
|
|
};
|