diff --git a/webapp/src/components/addContentMenuItem.test.tsx b/webapp/src/components/addContentMenuItem.test.tsx index 6c16e3a3a..e25a957cf 100644 --- a/webapp/src/components/addContentMenuItem.test.tsx +++ b/webapp/src/components/addContentMenuItem.test.tsx @@ -67,7 +67,7 @@ describe('components/addContentMenuItem', () => { expect(container).toMatchSnapshot() const buttonElement = screen.getByRole('button', {name: 'text'}) userEvent.click(buttonElement) - await waitFor(() => expect(mockedMutator.performAsUndoGroup).toBeCalled()) + await waitFor(() => expect(mockedMutator.insertBlock).toBeCalled()) }) test('return a checkbox menu item', async () => { @@ -83,7 +83,7 @@ describe('components/addContentMenuItem', () => { expect(container).toMatchSnapshot() const buttonElement = screen.getByRole('button', {name: 'checkbox'}) userEvent.click(buttonElement) - await waitFor(() => expect(mockedMutator.performAsUndoGroup).toBeCalled()) + await waitFor(() => expect(mockedMutator.insertBlock).toBeCalled()) }) test('return a divider menu item', async () => { @@ -99,7 +99,7 @@ describe('components/addContentMenuItem', () => { expect(container).toMatchSnapshot() const buttonElement = screen.getByRole('button', {name: 'divider'}) userEvent.click(buttonElement) - await waitFor(() => expect(mockedMutator.performAsUndoGroup).toBeCalled()) + await waitFor(() => expect(mockedMutator.insertBlock).toBeCalled()) }) test('return an error and empty element from unknown type', () => { diff --git a/webapp/src/components/addContentMenuItem.tsx b/webapp/src/components/addContentMenuItem.tsx index 3292f6f16..746c9182a 100644 --- a/webapp/src/components/addContentMenuItem.tsx +++ b/webapp/src/components/addContentMenuItem.tsx @@ -6,7 +6,9 @@ import {useIntl} from 'react-intl' import {BlockTypes} from '../blocks/block' import {Card} from '../blocks/card' +import {Block} from '../blocks/block' import mutator from '../mutator' +import octoClient from '../octoClient' import {Utils} from '../utils' import Menu from '../widgets/menu' @@ -21,7 +23,6 @@ type Props = { const AddContentMenuItem = (props:Props): JSX.Element => { const {card, type, cords} = props const index = cords.x - const contentOrder = card.fields.contentOrder.slice() const intl = useIntl() const handler = contentRegistry.getHandler(type) @@ -43,11 +44,19 @@ const AddContentMenuItem = (props:Props): JSX.Element => { const typeName = handler.getDisplayText(intl) const description = intl.formatMessage({id: 'ContentBlock.addElement', defaultMessage: 'add {type}'}, {type: typeName}) - mutator.performAsUndoGroup(async () => { - const insertedBlock = await mutator.insertBlock(newBlock.boardId, newBlock, description) - contentOrder.splice(index, 0, insertedBlock.id) - await mutator.changeCardContentOrder(card.boardId, card.id, card.fields.contentOrder, contentOrder, description) - }) + + const afterRedo = async (nb: Block) => { + const contentOrder = card.fields.contentOrder.slice() + contentOrder.splice(index, 0, nb.id) + await octoClient.patchBlock(card.boardId, card.id, {updatedFields: {contentOrder}}) + } + + const beforeUndo = async () => { + const contentOrder = card.fields.contentOrder.slice() + await octoClient.patchBlock(card.boardId, card.id, {updatedFields: {contentOrder}}) + } + + await mutator.insertBlock(newBlock.boardId, newBlock, description, afterRedo, beforeUndo) }} /> ) diff --git a/webapp/src/components/cardDetail/cardDetailContents.tsx b/webapp/src/components/cardDetail/cardDetailContents.tsx index 5bd468836..cb90cd6f1 100644 --- a/webapp/src/components/cardDetail/cardDetailContents.tsx +++ b/webapp/src/components/cardDetail/cardDetailContents.tsx @@ -6,7 +6,9 @@ import {useIntl, IntlShape} from 'react-intl' import {IContentBlockWithCords, ContentBlock as ContentBlockType} from '../../blocks/contentBlock' import {Card} from '../../blocks/card' import {createTextBlock} from '../../blocks/textBlock' +import {Block} from '../../blocks/block' import mutator from '../../mutator' +import octoClient from '../../octoClient' import {useSortableWithGrip} from '../../hooks/sortable' import ContentBlock from '../contentBlock' @@ -25,19 +27,26 @@ type Props = { readonly: boolean } -function addTextBlock(card: Card, intl: IntlShape, text: string): void { +async function addTextBlock(card: Card, intl: IntlShape, text: string): Promise { const block = createTextBlock() block.parentId = card.id block.boardId = card.boardId block.title = text - mutator.performAsUndoGroup(async () => { - const description = intl.formatMessage({id: 'CardDetail.addCardText', defaultMessage: 'add card text'}) - const insertedBlock = await mutator.insertBlock(block.boardId, block, description) + const description = intl.formatMessage({id: 'CardDetail.addCardText', defaultMessage: 'add card text'}) + + const afterRedo = async (newBlock: Block) => { const contentOrder = card.fields.contentOrder.slice() - contentOrder.push(insertedBlock.id) - await mutator.changeCardContentOrder(card.boardId, card.id, card.fields.contentOrder, contentOrder, description) - }) + contentOrder.push(newBlock.id) + await octoClient.patchBlock(card.boardId, card.id, {updatedFields: {contentOrder}}) + } + + const beforeUndo = async () => { + const contentOrder = card.fields.contentOrder.slice() + await octoClient.patchBlock(card.boardId, card.id, {updatedFields: {contentOrder}}) + } + + return mutator.insertBlock(block.boardId, block, description, afterRedo, beforeUndo) } function moveBlock(card: Card, srcBlock: IContentBlockWithCords, dstBlock: IContentBlockWithCords, intl: IntlShape, moveTo: Position): void { diff --git a/webapp/src/components/cardDetail/cardDetailContext.tsx b/webapp/src/components/cardDetail/cardDetailContext.tsx index 026cc99ea..38b87beaa 100644 --- a/webapp/src/components/cardDetail/cardDetailContext.tsx +++ b/webapp/src/components/cardDetail/cardDetailContext.tsx @@ -7,6 +7,7 @@ import {useIntl} from 'react-intl' import {Block} from '../../blocks/block' import {Card} from '../../blocks/card' import {ContentHandler} from '../content/contentRegistry' +import octoClient from '../../octoClient' import mutator from '../../mutator' export type AddedBlock = { @@ -50,14 +51,20 @@ export const CardDetailProvider = (props: CardDetailProps): ReactElement => { const typeName = handler.getDisplayText(intl) const description = intl.formatMessage({id: 'ContentBlock.addElement', defaultMessage: 'add {type}'}, {type: typeName}) await mutator.performAsUndoGroup(async () => { - const insertedBlock = await mutator.insertBlock(block.boardId, block, description) - const contentOrder = card.fields.contentOrder.slice() - contentOrder.splice(index, 0, insertedBlock.id) - setLastAddedBlock({ - id: insertedBlock.id, - autoAdded: auto, - }) - await mutator.changeCardContentOrder(card.boardId, card.id, card.fields.contentOrder, contentOrder, description) + + const afterRedo = async (newBlock: Block) => { + const contentOrder = card.fields.contentOrder.slice() + contentOrder.splice(index, 0, newBlock.id) + await octoClient.patchBlock(card.boardId, card.id, {updatedFields: {contentOrder}}) + } + + const beforeUndo = async () => { + const contentOrder = card.fields.contentOrder.slice() + await octoClient.patchBlock(card.boardId, card.id, {updatedFields: {contentOrder}}) + } + + const insertedBlock = await mutator.insertBlock(block.boardId, block, description, afterRedo, beforeUndo) + setLastAddedBlock({id: insertedBlock.id, autoAdded: auto}) }) }, [card.boardId, card.id, card.fields.contentOrder]) diff --git a/webapp/src/components/cardDetail/imagePaste.tsx b/webapp/src/components/cardDetail/imagePaste.tsx index e94386b6d..a708e676e 100644 --- a/webapp/src/components/cardDetail/imagePaste.tsx +++ b/webapp/src/components/cardDetail/imagePaste.tsx @@ -6,6 +6,7 @@ import {useIntl} from 'react-intl' import {ImageBlock, createImageBlock} from '../../blocks/imageBlock' import {sendFlashMessage} from '../flashMessages' +import {Block} from '../../blocks/block' import octoClient from '../../octoClient' import mutator from '../../mutator' @@ -45,13 +46,18 @@ export default function useImagePaste(boardId: string, cardId: string, contentOr sendFlashMessage({content: intl.formatMessage({id: 'imagePaste.upload-failed', defaultMessage: 'Some files not uploaded. File size limit reached'}), severity: 'normal'}) } - mutator.performAsUndoGroup(async () => { - const newContentBlocks = await mutator.insertBlocks(boardId, blocksToInsert, 'pasted images') + const afterRedo = async (newBlocks: Block[]) => { const newContentOrder = JSON.parse(JSON.stringify(contentOrder)) - newContentOrder.push(...newContentBlocks.map((b: ImageBlock) => b.id)) + newContentOrder.push(...newBlocks.map((b: Block) => b.id)) + await octoClient.patchBlock(boardId, cardId, {updatedFields: {contentOrder: newContentOrder}}) + } - await mutator.changeCardContentOrder(boardId, cardId, contentOrder, newContentOrder, 'paste image') - }) + const beforeUndo = async () => { + const newContentOrder = JSON.parse(JSON.stringify(contentOrder)) + await octoClient.patchBlock(boardId, cardId, {updatedFields: {contentOrder: newContentOrder}}) + } + + await mutator.insertBlocks(boardId, blocksToInsert, 'pasted images', afterRedo, beforeUndo) }, [cardId, contentOrder, boardId]) const onDrop = useCallback((event: DragEvent): void => {