diff --git a/src/components/Board/Items/App/AppMenu.tsx b/src/components/Board/Items/App/AppMenu.tsx index ed5cf565b..816769085 100644 --- a/src/components/Board/Items/App/AppMenu.tsx +++ b/src/components/Board/Items/App/AppMenu.tsx @@ -1,5 +1,5 @@ import { openRemoveItemModal, useItemActions } from '~/components/Board/Items/item-actions'; -import { type AppItem, useRequiredBoard } from '~/components/Board/context'; +import { type AppItem } from '~/components/Board/context'; import { useResizeGridItem } from '~/components/Board/gridstack/useResizeGridItem'; import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions'; @@ -11,8 +11,7 @@ interface AppMenuProps { } export const AppMenu = ({ app }: AppMenuProps) => { - const board = useRequiredBoard(); - const { removeItem } = useItemActions({ boardName: board.name }); + const { removeItem } = useItemActions(); const resizeGridItem = useResizeGridItem(); const handleClickEdit = () => { @@ -21,7 +20,6 @@ export const AppMenu = ({ app }: AppMenuProps) => { size: 'xl', innerProps: { app, - board, allowAppNamePropagation: false, }, styles: { @@ -37,7 +35,6 @@ export const AppMenu = ({ app }: AppMenuProps) => { modal: 'changeAppPositionModal', innerProps: { app, - boardName: board.name, resizeGridItem, }, styles: { diff --git a/src/components/Board/Items/App/ChangeAppPositionModal.tsx b/src/components/Board/Items/App/ChangeAppPositionModal.tsx index 2de49aa7f..4d4742b99 100644 --- a/src/components/Board/Items/App/ChangeAppPositionModal.tsx +++ b/src/components/Board/Items/App/ChangeAppPositionModal.tsx @@ -9,7 +9,6 @@ import { CommonChangePositionModal } from '../CommonChangePositionModal'; type ChangeAppPositionModalInnerProps = { app: AppItem; - boardName: string; resizeGridItem: ReturnType; }; @@ -18,7 +17,7 @@ export const ChangeAppPositionModal = ({ context, innerProps, }: ContextModalProps) => { - const { moveAndResizeItem } = useItemActions({ boardName: innerProps.boardName }); + const { moveAndResizeItem } = useItemActions(); const handleSubmit = (x: number, y: number, width: number, height: number) => { moveAndResizeItem({ diff --git a/src/components/Board/Items/App/EditAppModal.tsx b/src/components/Board/Items/App/EditAppModal.tsx index 779583ff0..d3496c60b 100644 --- a/src/components/Board/Items/App/EditAppModal.tsx +++ b/src/components/Board/Items/App/EditAppModal.tsx @@ -14,7 +14,6 @@ import { removeTrailingSlash } from 'next/dist/shared/lib/router/utils/remove-tr import { useState } from 'react'; import { z } from 'zod'; import { objectKeys } from '~/tools/object'; -import { RouterOutputs } from '~/utils/api'; import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; import { appFormSchema } from '~/validations/app'; @@ -28,7 +27,6 @@ import { useAppActions } from './app-actions'; export type EditAppModalInnerProps = { app: AppItem; - board: RouterOutputs['boards']['byName']; allowAppNamePropagation: boolean; }; @@ -39,7 +37,7 @@ export const EditAppModal = ({ }: ContextModalProps) => { const { t } = useTranslation(['layout/modals/add-app', 'common']); const [activeTab, setActiveTab] = useState('general'); - const { createOrUpdateApp } = useAppActions({ boardName: innerProps.board.name }); + const { createOrUpdateApp } = useAppActions(); // TODO: change to ref const [allowAppNamePropagation, setAllowAppNamePropagation] = useState( innerProps.allowAppNamePropagation diff --git a/src/components/Board/Items/App/app-actions.tsx b/src/components/Board/Items/App/app-actions.tsx index 0dbb91523..fd575280f 100644 --- a/src/components/Board/Items/App/app-actions.tsx +++ b/src/components/Board/Items/App/app-actions.tsx @@ -1,19 +1,19 @@ import { useCallback } from 'react'; import { z } from 'zod'; -import { api } from '~/utils/api'; import { appFormSchema } from '~/validations/app'; +import { useUpdateBoard } from '../../board-actions'; import { type AppItem, type EmptySection } from '../../context'; type CreateOrUpdateApp = { app: z.infer; }; -export const useAppActions = ({ boardName }: { boardName: string }) => { - const utils = api.useContext(); +export const useAppActions = () => { + const updateBoard = useUpdateBoard(); const createOrUpdateApp = useCallback( ({ app }: CreateOrUpdateApp) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; let sectionId = prev.sections.find((section) => @@ -40,7 +40,7 @@ export const useAppActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); return { diff --git a/src/components/Board/Items/Widget/ChangeWidgetPositionModal.tsx b/src/components/Board/Items/Widget/ChangeWidgetPositionModal.tsx index ea17391a1..5a69411e9 100644 --- a/src/components/Board/Items/Widget/ChangeWidgetPositionModal.tsx +++ b/src/components/Board/Items/Widget/ChangeWidgetPositionModal.tsx @@ -10,7 +10,6 @@ import { CommonChangePositionModal } from '../CommonChangePositionModal'; export type WidgetChangePositionModalInnerProps = { widget: WidgetItem; - boardName: string; wrapperColumnCount: number; resizeGridItem: ReturnType; }; @@ -20,7 +19,7 @@ export const ChangeWidgetPositionModal = ({ id, innerProps, }: ContextModalProps) => { - const { moveAndResizeItem } = useItemActions({ boardName: innerProps.boardName }); + const { moveAndResizeItem } = useItemActions(); const handleSubmit = (x: number, y: number, width: number, height: number) => { moveAndResizeItem({ diff --git a/src/components/Board/Items/Widget/WidgetsEditModal.tsx b/src/components/Board/Items/Widget/WidgetsEditModal.tsx index b61848b98..b29fac1e8 100644 --- a/src/components/Board/Items/Widget/WidgetsEditModal.tsx +++ b/src/components/Board/Items/Widget/WidgetsEditModal.tsx @@ -38,7 +38,6 @@ export type WidgetEditModalInnerProps = { widgetType: string; options: Record; widgetOptions: IWidgetDefinition['options']; - boardName: string; }; export const WidgetsEditModal = ({ @@ -52,7 +51,7 @@ export const WidgetsEditModal = ({ // Find the Key in the "Widgets" Object that matches the widgetId const currentWidgetDefinition = Widgets[innerProps.widgetType as keyof typeof Widgets]; - const { updateWidgetOptions } = useWidgetActions({ boardName: innerProps.boardName }); + const { updateWidgetOptions } = useWidgetActions(); if (!innerProps.options) return null; diff --git a/src/components/Board/Items/Widget/WidgetsMenu.tsx b/src/components/Board/Items/Widget/WidgetsMenu.tsx index d95e89892..deb4d9176 100644 --- a/src/components/Board/Items/Widget/WidgetsMenu.tsx +++ b/src/components/Board/Items/Widget/WidgetsMenu.tsx @@ -1,7 +1,7 @@ import { Title } from '@mantine/core'; import { useTranslation } from 'next-i18next'; import { openRemoveItemModal, useItemActions } from '~/components/Board/Items/item-actions'; -import { type WidgetItem, useRequiredBoard } from '~/components/Board/context'; +import { type WidgetItem } from '~/components/Board/context'; import { useResizeGridItem } from '~/components/Board/gridstack/useResizeGridItem'; import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions'; @@ -17,10 +17,9 @@ interface WidgetsMenuProps { export const WidgetsMenu = ({ widget }: WidgetsMenuProps) => { const { t } = useTranslation(`modules/${widget.sort}`); - const board = useRequiredBoard(); const wrapperColumnCount = useWrapperColumnCount(); const resizeGridItem = useResizeGridItem(); - const { removeItem } = useItemActions({ boardName: board.name }); + const { removeItem } = useItemActions(); if (!widget || !wrapperColumnCount) return null; // Then get the widget definition @@ -44,7 +43,6 @@ export const WidgetsMenu = ({ widget }: WidgetsMenuProps) => { title: null, innerProps: { widget, - boardName: board.name, wrapperColumnCount, resizeGridItem, }, @@ -59,7 +57,6 @@ export const WidgetsMenu = ({ widget }: WidgetsMenuProps) => { widgetId: widget.id, widgetType: widget.sort, options: widget.options, - boardName: board.name, widgetOptions: widgetDefinitionObject.options, }, zIndex: 250, diff --git a/src/components/Board/Items/Widget/widget-actions.ts b/src/components/Board/Items/Widget/widget-actions.ts index b59fd9a1d..c8cad8ea3 100644 --- a/src/components/Board/Items/Widget/widget-actions.ts +++ b/src/components/Board/Items/Widget/widget-actions.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; import { v4 } from 'uuid'; import { z } from 'zod'; -import { api } from '~/utils/api'; import { widgetCreationSchema, widgetSortSchema } from '~/validations/widget'; import { IWidgetDefinition } from '~/widgets/widgets'; +import { useUpdateBoard } from '../../board-actions'; import { EmptySection, WidgetItem } from '../../context'; type UpdateWidgetOptions = { @@ -17,11 +17,11 @@ type CreateWidget = { definition: IWidgetDefinition; }; -export const useWidgetActions = ({ boardName }: { boardName: string }) => { - const utils = api.useContext(); +export const useWidgetActions = () => { + const updateBoard = useUpdateBoard(); const updateWidgetOptions = useCallback( ({ itemId, newOptions }: UpdateWidgetOptions) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; return { ...prev, @@ -43,12 +43,12 @@ export const useWidgetActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); const createWidget = useCallback( ({ sort, definition }: CreateWidget) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; let lastSection = prev.sections @@ -82,7 +82,7 @@ export const useWidgetActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); return { diff --git a/src/components/Board/Items/item-actions.tsx b/src/components/Board/Items/item-actions.tsx index 097e920f7..7b37f7392 100644 --- a/src/components/Board/Items/item-actions.tsx +++ b/src/components/Board/Items/item-actions.tsx @@ -2,7 +2,8 @@ import { Text, Title } from '@mantine/core'; import { openConfirmModal } from '@mantine/modals'; import { useCallback } from 'react'; import { Trans } from 'react-i18next'; -import { api } from '~/utils/api'; + +import { useUpdateBoard } from '../board-actions'; type MoveAndResizeItem = { itemId: string; @@ -23,11 +24,11 @@ type RemoveItem = { itemId: string; }; -export const useItemActions = ({ boardName }: { boardName: string }) => { - const utils = api.useContext(); +export const useItemActions = () => { + const updateBoard = useUpdateBoard(); const moveAndResizeItem = useCallback( ({ itemId, ...positionProps }: MoveAndResizeItem) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; return { ...prev, @@ -49,12 +50,12 @@ export const useItemActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); const moveItemToSection = useCallback( ({ itemId, sectionId, ...positionProps }: MoveItemToSection) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; const currentSection = prev.sections.find((section) => @@ -99,12 +100,12 @@ export const useItemActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); const removeItem = useCallback( ({ itemId }: RemoveItem) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; return { ...prev, @@ -116,7 +117,7 @@ export const useItemActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); return { @@ -155,16 +156,3 @@ export const openRemoveItemModal = ({ name, onConfirm }: OpenRemoveItemModalProp onConfirm, }); }; - -/* -- Add category (on top, below, above) -- Rename category -- Move category (down & up) -- Remove category -- Add widget -- Edit widget -- Remove widget -- Add app -- Edit app -- Remove app -*/ diff --git a/src/components/Board/Sections/Category/Actions/category-actions.ts b/src/components/Board/Sections/Category/Actions/category-actions.ts index 7d12d1656..1ae63f954 100644 --- a/src/components/Board/Sections/Category/Actions/category-actions.ts +++ b/src/components/Board/Sections/Category/Actions/category-actions.ts @@ -1,7 +1,7 @@ import Consola from 'consola'; import { useCallback } from 'react'; import { v4 } from 'uuid'; -import { api } from '~/utils/api'; +import { useUpdateBoard } from '~/components/Board/board-actions'; import { type CategorySection, type EmptySection } from '../../../context'; @@ -24,8 +24,8 @@ type RemoveCategory = { id: string; }; -export const useCategoryActions = ({ boardName }: { boardName: string }) => { - const utils = api.useContext(); +export const useCategoryActions = () => { + const updateBoard = useUpdateBoard(); const addCategory = useCallback( ({ name, position }: AddCategory) => { @@ -33,7 +33,7 @@ export const useCategoryActions = ({ boardName }: { boardName: string }) => { Consola.error('Cannot add category before first section'); return; } - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; return { ...prev, @@ -76,12 +76,58 @@ export const useCategoryActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] + ); + + const addCategoryToEnd = useCallback( + ({ name }: { name: string }) => { + updateBoard((prev) => { + if (!prev) return prev; + + const lastSection = prev.sections + .filter( + (x): x is CategorySection | EmptySection => x.kind === 'empty' || x.kind === 'category' + ) + .sort((a, b) => b.position - a.position) + .at(0); + + if (!lastSection) return prev; + const lastPosition = lastSection.position; + + return { + ...prev, + sections: [ + // Ignore sidebar and hidden sections + ...prev.sections.filter( + (section) => section.kind === 'sidebar' || section.kind === 'hidden' + ), + // Place sections before the new category + ...prev.sections.filter((section) => section.kind === 'category'), + { + id: v4(), + name, + kind: 'category', + position: lastPosition + 1, + items: [], + }, + { + id: v4(), + kind: 'empty', + position: lastPosition + 2, + items: [], + }, + // Place sections after the new category + ...prev.sections.filter((section) => section.kind === 'empty'), + ], + }; + }); + }, + [updateBoard] ); const renameCategory = useCallback( ({ id: categoryId, name }: RenameCategory) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; return { ...prev, @@ -96,12 +142,12 @@ export const useCategoryActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); const moveCategory = useCallback( ({ id, direction }: MoveCategory) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; const currentCategory = prev.sections.find( @@ -155,12 +201,12 @@ export const useCategoryActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); const removeCategory = useCallback( ({ id: categoryId }: RemoveCategory) => { - utils.boards.byName.setData({ boardName, userAgent: navigator.userAgent }, (prev) => { + updateBoard((prev) => { if (!prev) return prev; const currentCategory = prev.sections.find( @@ -194,11 +240,12 @@ export const useCategoryActions = ({ boardName }: { boardName: string }) => { }; }); }, - [boardName, utils] + [updateBoard] ); return { addCategory, + addCategoryToEnd, renameCategory, moveCategory, removeCategory, diff --git a/src/components/Board/Sections/Category/Actions/category-menu-actions.tsx b/src/components/Board/Sections/Category/Actions/category-menu-actions.tsx index 90ddb198c..1d5c755da 100644 --- a/src/components/Board/Sections/Category/Actions/category-menu-actions.tsx +++ b/src/components/Board/Sections/Category/Actions/category-menu-actions.tsx @@ -1,16 +1,12 @@ import { v4 as uuidv4 } from 'uuid'; import { useCategoryActions } from '~/components/Board/Sections/Category/Actions/category-actions'; -import { useRequiredBoard } from '~/components/Board/context'; import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions'; import { CategoryType } from '~/types/category'; import { CategoryEditModalInnerProps } from '../CategoryEditModal'; export const useCategoryMenuActions = (category: CategoryType) => { - const boardName = useRequiredBoard().name; - const { addCategory, moveCategory, removeCategory, renameCategory } = useCategoryActions({ - boardName, - }); + const { addCategory, moveCategory, removeCategory, renameCategory } = useCategoryActions(); // creates a new category above the current const addCategoryAbove = () => { diff --git a/src/components/Board/SelectElement/Overview/AvailableElementsOverview.tsx b/src/components/Board/SelectElement/Overview/AvailableElementsOverview.tsx index 1c418df34..af844c6b1 100644 --- a/src/components/Board/SelectElement/Overview/AvailableElementsOverview.tsx +++ b/src/components/Board/SelectElement/Overview/AvailableElementsOverview.tsx @@ -9,33 +9,25 @@ import { v4 as uuidv4 } from 'uuid'; import { CategoryEditModalInnerProps } from '~/components/Board/Sections/Category/CategoryEditModal'; import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions'; import { generateDefaultApp2 } from '~/tools/shared/app'; -import { RouterOutputs } from '~/utils/api'; import { EditAppModalInnerProps } from '../../Items/App/EditAppModal'; import { useCategoryActions } from '../../Sections/Category/Actions/category-actions'; -import { AppItem, CategorySection, EmptySection } from '../../context'; +import { AppItem } from '../../context'; import { useStyles } from '../Shared/styles'; interface AvailableElementTypesProps { modalId: string; - board: RouterOutputs['boards']['byName']; onOpenIntegrations: () => void; } export const AvailableElementTypes = ({ modalId, - board, onOpenIntegrations: onOpenWidgets, }: AvailableElementTypesProps) => { const { t } = useTranslation('layout/element-selector/selector'); - const { addCategory } = useCategoryActions({ boardName: board.name }); - const lastSection = board.sections - .filter((x): x is CategorySection | EmptySection => x.kind === 'empty' || x.kind === 'category') - .sort((a, b) => b.position - a.position) - .at(0); + const { addCategoryToEnd } = useCategoryActions(); const onClickCreateCategory = async () => { - if (!lastSection) return; openContextModalGeneric({ modal: 'categoryEditModal', title: t('category.newName'), @@ -47,7 +39,7 @@ export const AvailableElementTypes = ({ position: 0, // doesn't matter, is being overwritten }, onSuccess: async (category) => { - addCategory({ name: category.name, position: lastSection.position + 1 }); + addCategoryToEnd({ name: category.name }); closeModal(modalId); showNotification({ title: t('category.created.title'), @@ -72,7 +64,6 @@ export const AvailableElementTypes = ({ modal: 'editApp', innerProps: { app: generateDefaultApp2() as AppItem, - board: board, allowAppNamePropagation: true, }, size: 'xl', diff --git a/src/components/Board/SelectElement/SelectElementModal.tsx b/src/components/Board/SelectElement/SelectElementModal.tsx index c9e7f0398..7e2b9661b 100644 --- a/src/components/Board/SelectElement/SelectElementModal.tsx +++ b/src/components/Board/SelectElement/SelectElementModal.tsx @@ -1,32 +1,21 @@ import { ContextModalProps } from '@mantine/modals'; import { useState } from 'react'; -import { RouterOutputs } from '~/utils/api'; import { AvailableElementTypes } from './Overview/AvailableElementsOverview'; import { AvailableIntegrationElements } from './WidgetsTab/AvailableWidgetsTab'; -type InnerProps = { - board: RouterOutputs['boards']['byName']; -}; +type InnerProps = {}; export const SelectElementModal = ({ id, innerProps }: ContextModalProps) => { const [activeTab, setActiveTab] = useState(); if (activeTab === 'integrations') { return ( - setActiveTab(undefined)} - /> + setActiveTab(undefined)} /> ); } return ( - setActiveTab('integrations')} - /> + setActiveTab('integrations')} /> ); }; diff --git a/src/components/Board/SelectElement/Shared/GenericElementType.tsx b/src/components/Board/SelectElement/Shared/GenericElementType.tsx index 370f13cd4..c87e5ef80 100644 --- a/src/components/Board/SelectElement/Shared/GenericElementType.tsx +++ b/src/components/Board/SelectElement/Shared/GenericElementType.tsx @@ -7,7 +7,6 @@ import { useStyles } from './styles'; interface GenericAvailableElementTypeProps { name: string; - id: string; handleAddition: () => Promise; description?: string; image: string | Icon; @@ -16,7 +15,6 @@ interface GenericAvailableElementTypeProps { export const GenericAvailableElementType = ({ name, - id, description, image, disabled, diff --git a/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx b/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx index 5b4ee516e..767fbc27b 100644 --- a/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx +++ b/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx @@ -8,13 +8,11 @@ import { WidgetElementType } from './WidgetElementType'; interface AvailableIntegrationElementsProps { modalId: string; - boardName: string; onClickBack: () => void; } export const AvailableIntegrationElements = ({ modalId, - boardName, onClickBack, }: AvailableIntegrationElementsProps) => { const { t } = useTranslation('layout/element-selector/selector'); @@ -28,14 +26,7 @@ export const AvailableIntegrationElements = ({ {objectEntries(widgets).map(([k, v]) => ( - + ))} diff --git a/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx b/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx index 125efcdfd..5a4163607 100644 --- a/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx +++ b/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx @@ -10,7 +10,6 @@ import { GenericAvailableElementType } from '../Shared/GenericElementType'; interface WidgetElementTypeProps { sort: WidgetSort; - boardName: string; image: string | Icon; disabled?: boolean; widget: IWidgetDefinition; @@ -22,12 +21,11 @@ export const WidgetElementType = ({ image, disabled, widget, - boardName, modalId, }: WidgetElementTypeProps) => { const { closeModal } = useModals(); const { t } = useTranslation(`modules/${sort}`); - const { createWidget } = useWidgetActions({ boardName }); + const { createWidget } = useWidgetActions(); const handleAddition = async () => { createWidget({ diff --git a/src/components/Board/board-actions.ts b/src/components/Board/board-actions.ts new file mode 100644 index 000000000..ba33137a7 --- /dev/null +++ b/src/components/Board/board-actions.ts @@ -0,0 +1,19 @@ +import { useCallback } from 'react'; +import { api } from '~/utils/api'; + +import { useRequiredBoardProps } from './outer-context'; + +export const useUpdateBoard = () => { + const utils = api.useUtils(); + const { boardName, layoutId } = useRequiredBoardProps(); + + return useCallback( + (callback: Parameters<(typeof utils)['boards']['byName']['setData']>[1]) => { + utils.boards.byName.setData( + { boardName, layoutId, userAgent: navigator.userAgent }, + callback + ); + }, + [boardName, layoutId, utils] + ); +}; diff --git a/src/components/Board/context.tsx b/src/components/Board/context.tsx index 262b5e731..136289b99 100644 --- a/src/components/Board/context.tsx +++ b/src/components/Board/context.tsx @@ -3,10 +3,10 @@ import Router, { useRouter } from 'next/router'; import { createContext, useContext, useEffect } from 'react'; import { RouterOutputs, api } from '~/utils/api'; +import { useRequiredBoardProps } from './outer-context'; import { useEditModeStore } from './useEditModeStore'; type BoardContextType = { - layout?: string; board: RouterOutputs['boards']['byName']; }; const BoardContext = createContext(null); @@ -16,6 +16,7 @@ type BoardProviderProps = { children: React.ReactNode; }; export const BoardProvider = ({ children, userAgent, ...props }: BoardProviderProps) => { + const { setProps } = useRequiredBoardProps(); const { enabled } = useEditModeStore(); const router = useRouter(); const { layout } = router.query; @@ -32,6 +33,16 @@ export const BoardProvider = ({ children, userAgent, ...props }: BoardProviderPr } ); + // Setting props for the outer context so they can be used within modals. + useEffect(() => { + if (!queryBoard) return; + setProps({ + boardName: queryBoard.name, + boardId: queryBoard.id, + layoutId: layout as string | undefined, + }); + }, [layout, queryBoard?.name, queryBoard?.id, setProps]); + const board = queryBoard ?? props.initialBoard; // Initialdata property is not working because it somehow ignores the enabled property. useConfirmLeavePage(enabled); @@ -39,7 +50,6 @@ export const BoardProvider = ({ children, userAgent, ...props }: BoardProviderPr return ( @@ -49,9 +59,9 @@ export const BoardProvider = ({ children, userAgent, ...props }: BoardProviderPr }; export const useRequiredBoard = () => { - const ctx = useContext(BoardContext); - if (!ctx) throw new Error('useBoard must be used within a BoardProvider'); - return ctx.board; + const optionalBoard = useOptionalBoard(); + if (!optionalBoard) throw new Error('useBoard must be used within a BoardProvider'); + return optionalBoard; }; export const useOptionalBoard = () => { diff --git a/src/components/Board/gridstack/use-gridstack.ts b/src/components/Board/gridstack/use-gridstack.ts index 163830f40..daca89d45 100644 --- a/src/components/Board/gridstack/use-gridstack.ts +++ b/src/components/Board/gridstack/use-gridstack.ts @@ -8,7 +8,7 @@ import { useRef, } from 'react'; import { useItemActions } from '~/components/Board/Items/item-actions'; -import { type Section, useRequiredBoard } from '~/components/Board/context'; +import { type Section } from '~/components/Board/context'; import { useEditModeStore } from '../useEditModeStore'; import { initializeGridstack } from './init-gridstack'; @@ -28,8 +28,7 @@ type UseGridstackProps = { export const useGridstack = ({ section }: UseGridstackProps): UseGristackReturnType => { const isEditMode = useEditModeStore((x) => x.enabled); - const board = useRequiredBoard(); - const { moveAndResizeItem, moveItemToSection } = useItemActions({ boardName: board.name }); + const { moveAndResizeItem, moveItemToSection } = useItemActions(); // define reference for wrapper - is used to calculate the width of the wrapper const wrapperRef = useRef(null); // references to the diffrent items contained in the gridstack diff --git a/src/components/Board/outer-context.tsx b/src/components/Board/outer-context.tsx new file mode 100644 index 000000000..8f9481dc8 --- /dev/null +++ b/src/components/Board/outer-context.tsx @@ -0,0 +1,43 @@ +import { Dispatch, ReactNode, SetStateAction, createContext, useContext, useState } from 'react'; + +type BoardProps = { + boardName: string; + boardId: string; + layoutId?: string; +}; + +type OuterBoardContextType = Partial & { + setProps: Dispatch>; +}; + +const OuterBoardContext = createContext(null); + +export const OuterBoardProvider = ({ children }: { children: ReactNode }) => { + const [props, setProps] = useState(); + + return ( + + {children} + + ); +}; + +export const useRequiredBoardProps = () => { + const optionalBoard = useOptionalBoardProps(); + if (!optionalBoard) throw new Error('useBoard must be used within a BoardProvider'); + return { + boardName: optionalBoard.boardName!, + boardId: optionalBoard.boardId!, + ...optionalBoard, + }; +}; + +export const useOptionalBoardProps = () => { + const ctx = useContext(OuterBoardContext); + return ctx; +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ab7fd3feb..1abe226ec 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -20,6 +20,7 @@ import { type AppProps } from 'next/app'; import Script from 'next/script'; import { useEffect, useState } from 'react'; import 'video.js/dist/video-js.css'; +import { OuterBoardProvider } from '~/components/Board/outer-context'; import { CommonHead } from '~/components/layout/Meta/CommonHead'; import { env } from '~/env.js'; import { ColorSchemeProvider } from '~/hooks/use-colorscheme'; @@ -148,9 +149,11 @@ function App( withCSSVariables > - - - + + + + + )} diff --git a/src/widgets/notebook/NotebookEditor.tsx b/src/widgets/notebook/NotebookEditor.tsx index 93ac7f0bd..562905be5 100644 --- a/src/widgets/notebook/NotebookEditor.tsx +++ b/src/widgets/notebook/NotebookEditor.tsx @@ -52,6 +52,7 @@ import StarterKit from '@tiptap/starter-kit'; import { useSession } from 'next-auth/react'; import { useTranslation } from 'next-i18next'; import { Dispatch, SetStateAction, useState } from 'react'; +import { useUpdateBoard } from '~/components/Board/board-actions'; import { useRequiredBoard } from '~/components/Board/context'; import { useEditModeStore } from '~/components/Board/useEditModeStore'; import { useColorTheme } from '~/tools/color'; @@ -70,7 +71,7 @@ export function Editor({ widget }: { widget: INotebookWidget }) { const [isEditing, setIsEditing] = useState(false); const board = useRequiredBoard(); - const utils = api.useContext(); + const updateBoard = useUpdateBoard(); const { primaryColor } = useColorTheme(); const { mutateAsync } = api.notebook.update.useMutation(); @@ -169,32 +170,29 @@ export function Editor({ widget }: { widget: INotebookWidget }) { editor.setEditable(current); if (current) return current; - utils.boards.byName.setData( - { boardName: board.name, userAgent: navigator.userAgent }, - (previous) => { - if (!previous) return previous; - return { - ...previous, - sections: previous.sections.map((section) => { - if (!section.items.some((item) => item.id === widget.id)) return section; - return { - ...section, - items: section.items.map((item) => { - if (item.id !== widget.id) return item; - const notebookEditor = item as INotebookWidget; - return { - ...notebookEditor, - options: { - ...notebookEditor.options, - content: debouncedContent, - }, - }; - }), - }; - }), - }; - } - ); + updateBoard((previous) => { + if (!previous) return previous; + return { + ...previous, + sections: previous.sections.map((section) => { + if (!section.items.some((item) => item.id === widget.id)) return section; + return { + ...section, + items: section.items.map((item) => { + if (item.id !== widget.id) return item; + const notebookEditor = item as INotebookWidget; + return { + ...notebookEditor, + options: { + ...notebookEditor.options, + content: debouncedContent, + }, + }; + }), + }; + }), + }; + }); setToSaveContent(contentUpdate);