♻️ Improve updating of client side board state

This commit is contained in:
Meier Lukas
2023-11-29 21:56:56 +01:00
parent 003a232f50
commit adaa4a5b64
22 changed files with 207 additions and 148 deletions

View File

@@ -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: {

View File

@@ -9,7 +9,6 @@ import { CommonChangePositionModal } from '../CommonChangePositionModal';
type ChangeAppPositionModalInnerProps = {
app: AppItem;
boardName: string;
resizeGridItem: ReturnType<typeof useResizeGridItem>;
};
@@ -18,7 +17,7 @@ export const ChangeAppPositionModal = ({
context,
innerProps,
}: ContextModalProps<ChangeAppPositionModalInnerProps>) => {
const { moveAndResizeItem } = useItemActions({ boardName: innerProps.boardName });
const { moveAndResizeItem } = useItemActions();
const handleSubmit = (x: number, y: number, width: number, height: number) => {
moveAndResizeItem({

View File

@@ -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<EditAppModalInnerProps>) => {
const { t } = useTranslation(['layout/modals/add-app', 'common']);
const [activeTab, setActiveTab] = useState<EditAppModalTab>('general');
const { createOrUpdateApp } = useAppActions({ boardName: innerProps.board.name });
const { createOrUpdateApp } = useAppActions();
// TODO: change to ref
const [allowAppNamePropagation, setAllowAppNamePropagation] = useState<boolean>(
innerProps.allowAppNamePropagation

View File

@@ -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<typeof appFormSchema>;
};
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 {

View File

@@ -10,7 +10,6 @@ import { CommonChangePositionModal } from '../CommonChangePositionModal';
export type WidgetChangePositionModalInnerProps = {
widget: WidgetItem;
boardName: string;
wrapperColumnCount: number;
resizeGridItem: ReturnType<typeof useResizeGridItem>;
};
@@ -20,7 +19,7 @@ export const ChangeWidgetPositionModal = ({
id,
innerProps,
}: ContextModalProps<WidgetChangePositionModalInnerProps>) => {
const { moveAndResizeItem } = useItemActions({ boardName: innerProps.boardName });
const { moveAndResizeItem } = useItemActions();
const handleSubmit = (x: number, y: number, width: number, height: number) => {
moveAndResizeItem({

View File

@@ -38,7 +38,6 @@ export type WidgetEditModalInnerProps = {
widgetType: string;
options: Record<string, unknown>;
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;

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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
*/

View File

@@ -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,

View File

@@ -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 = () => {

View File

@@ -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<CategoryEditModalInnerProps>({
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',

View File

@@ -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<InnerProps>) => {
const [activeTab, setActiveTab] = useState<undefined | 'integrations'>();
if (activeTab === 'integrations') {
return (
<AvailableIntegrationElements
modalId={id}
boardName={innerProps.board.name}
onClickBack={() => setActiveTab(undefined)}
/>
<AvailableIntegrationElements modalId={id} onClickBack={() => setActiveTab(undefined)} />
);
}
return (
<AvailableElementTypes
modalId={id}
board={innerProps.board}
onOpenIntegrations={() => setActiveTab('integrations')}
/>
<AvailableElementTypes modalId={id} onOpenIntegrations={() => setActiveTab('integrations')} />
);
};

View File

@@ -7,7 +7,6 @@ import { useStyles } from './styles';
interface GenericAvailableElementTypeProps {
name: string;
id: string;
handleAddition: () => Promise<void>;
description?: string;
image: string | Icon;
@@ -16,7 +15,6 @@ interface GenericAvailableElementTypeProps {
export const GenericAvailableElementType = ({
name,
id,
description,
image,
disabled,

View File

@@ -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 = ({
<Grid>
{objectEntries(widgets).map(([k, v]) => (
<WidgetElementType
key={k}
sort={k}
boardName={boardName}
image={v.icon}
widget={v}
modalId={modalId}
/>
<WidgetElementType key={k} sort={k} image={v.icon} widget={v} modalId={modalId} />
))}
</Grid>
</>

View File

@@ -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({

View File

@@ -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]
);
};

View File

@@ -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<BoardContextType | null>(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 (
<BoardContext.Provider
value={{
...props,
board: board!,
}}
>
@@ -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 = () => {

View File

@@ -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<HTMLDivElement>(null);
// references to the diffrent items contained in the gridstack

View File

@@ -0,0 +1,43 @@
import { Dispatch, ReactNode, SetStateAction, createContext, useContext, useState } from 'react';
type BoardProps = {
boardName: string;
boardId: string;
layoutId?: string;
};
type OuterBoardContextType = Partial<BoardProps> & {
setProps: Dispatch<SetStateAction<BoardProps | undefined>>;
};
const OuterBoardContext = createContext<OuterBoardContextType | null>(null);
export const OuterBoardProvider = ({ children }: { children: ReactNode }) => {
const [props, setProps] = useState<BoardProps>();
return (
<OuterBoardContext.Provider
value={{
...props,
setProps,
}}
>
{children}
</OuterBoardContext.Provider>
);
};
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;
};

View File

@@ -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
>
<Notifications limit={4} position="bottom-left" />
<ModalsProvider modals={modals}>
<Component {...pageProps} />
</ModalsProvider>
<OuterBoardProvider>
<ModalsProvider modals={modals}>
<Component {...pageProps} />
</ModalsProvider>
</OuterBoardProvider>
</MantineProvider>
</ColorTheme.Provider>
)}

View File

@@ -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);