From ebb4c33d6eb635c32f47d26cbc6ffa776203920f Mon Sep 17 00:00:00 2001 From: Nikita Teremovskiy Date: Mon, 25 Nov 2024 17:55:21 +0300 Subject: [PATCH 1/2] feat: Document popups: add document-context, modals-confirm-mutation and modal-confirm-document --- .../components/modals-confirm-document.tsx | 213 +++++++++++++ .../components/modals-confirm-mutation.tsx | 198 ++++++++++++ .../components/modals-confirm.tsx | 292 +++++------------- .../components/mutation-editor-modal.tsx | 5 +- .../multitable-panel/multitable-panel.tsx | 42 ++- .../document/dtos/document-commit.dto.ts | 2 +- .../document/dtos/document-create.dto.ts | 2 +- .../services/document/dtos/document.dto.ts | 2 +- libs/engine/src/app/app.tsx | 39 +-- .../src/app/components/context-manager.tsx | 65 +++- .../document-context/document-context.tsx | 27 ++ .../document-context/document-provider.tsx | 18 ++ .../app/contexts/document-context/index.ts | 3 + .../contexts/document-context/use-document.ts | 6 + libs/engine/src/index.ts | 1 + .../src/mini-overlay/index.tsx | 2 +- .../src/mini-overlay/side-panel.tsx | 37 ++- 17 files changed, 700 insertions(+), 254 deletions(-) create mode 100644 apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx create mode 100644 apps/extension/src/contentscript/multitable-panel/components/modals-confirm-mutation.tsx create mode 100644 libs/engine/src/app/contexts/document-context/document-context.tsx create mode 100644 libs/engine/src/app/contexts/document-context/document-provider.tsx create mode 100644 libs/engine/src/app/contexts/document-context/index.ts create mode 100644 libs/engine/src/app/contexts/document-context/use-document.ts diff --git a/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx new file mode 100644 index 00000000..5001f022 --- /dev/null +++ b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx @@ -0,0 +1,213 @@ +import { + AppId, + BaseDto, + DocumentMetadata, + EntitySourceType, + MutationCreateDto, + MutationDto, +} from '@mweb/backend' +import { + useAppDocuments, + useCreateMutation, + useDeleteLocalMutation, + useDocument, + useEditMutation, + useMutableWeb, +} from '@mweb/engine' +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' +import { cloneDeep } from '../../helpers' +import { useEscape } from '../../hooks/use-escape' +import { AlertProps } from './alert' +import { ModalsConfirm, ModalMode } from './modals-confirm' +import { DocumentTaskStatus } from '@mweb/engine/lib/app/contexts/document-context' + +export interface Props { + editingDocument: BaseDto & { + metadata: DocumentMetadata + openWith: AppId[] + content: any + } + loggedInAccountId: string + onCloseCurrent: () => void + onCloseAll: () => void +} + +interface IAlert extends AlertProps { + id: string +} + +// ToDo: duplication -- move somewhere +const alerts: { [name: string]: IAlert } = { + noWallet: { + id: 'noWallet', + text: 'Connect the NEAR wallet to create the document.', + severity: 'warning', + }, + emptyDocument: { + id: 'emptyDocument', + text: 'A document cannot be empty.', + severity: 'warning', + }, + notEditedDocument: { + id: 'notEditedDocument', + text: 'No changes found!', + severity: 'warning', + }, + idIsNotUnique: { + id: 'idIsNotUnique', + text: 'This document ID already exists.', + severity: 'warning', + }, + noName: { + id: 'noName', + text: 'Name must be specified.', + severity: 'error', + }, + noImage: { + id: 'noImage', + text: 'Image must be specified.', + severity: 'error', + }, +} + +export const ModalConfirmDocument: FC = ({ + editingDocument, + loggedInAccountId, + onCloseCurrent, + onCloseAll, +}) => { + const { name, image, description, fork_of } = editingDocument.metadata + + // Close modal with escape key + useEscape(onCloseCurrent) // ToDo -- does not work + + const [newName, setName] = useState(name ?? '') + const [newImage, setImage] = useState<{ ipfs_cid?: string } | undefined>(image) + const [newDescription, setDescription] = useState(description ?? '') + const [isApplyToOriginChecked, setIsApplyToOriginChecked] = useState(false) // ToDo: separate checkboxes + const [alert, setAlert] = useState(null) + const appDocuments = editingDocument.openWith && useAppDocuments(editingDocument.openWith[0]) // ToDo: need to change when different apps could use same documents + + const [mode, setMode] = useState( + !editingDocument.authorId // Newly created local document doesn't have author + ? ModalMode.Creating + : editingDocument.authorId === loggedInAccountId + ? ModalMode.Editing + : ModalMode.Forking + ) + + const forkedDocument = useMemo(() => { + if (mode !== ModalMode.Editing || !fork_of || !appDocuments) return + return appDocuments.documents?.find((doc) => doc.id === fork_of) + }, [fork_of, appDocuments, mode]) + + const { createMutation, isLoading: isCreating } = useCreateMutation() + const { editMutation, isLoading: isEditing } = useEditMutation() + const { deleteLocalMutation } = useDeleteLocalMutation() + const { documentTask, setDocumentTask } = useDocument() + + const isFormDisabled = isCreating || isEditing + + useEffect(() => setAlert(null), [newName, newImage, newDescription, isApplyToOriginChecked]) + + // const checkIfModified = useCallback( + // (mutationToPublish: MutationDto) => + // baseMutation ? !compareMutations(baseMutation, mutationToPublish) : true, + // [baseMutation] + // ) + + const doChecksForAlerts = useCallback( + (mutationToPublish: MutationCreateDto | MutationDto, isEditing: boolean): IAlert | null => { + if (!mutationToPublish.metadata.name) return alerts.noName + if (!mutationToPublish.metadata.image) return alerts.noImage + // if ( + // isEditing && + // !isApplyToOriginChecked && + // !checkIfModified(mutationToPublish as MutationDto) + // ) + // return alerts.notEditedMutation + return null + }, + [newName, newImage, isApplyToOriginChecked] // checkIfModified + ) + + const handleSaveClick = async () => { + if (!documentTask) return + console.log('handleSaveClick') + const documentToPublish = cloneDeep(editingDocument) + documentToPublish.metadata.name = newName.trim() + documentToPublish.metadata.image = newImage + documentToPublish.metadata.description = newDescription.trim() + setDocumentTask({ document: documentToPublish, appInstanceId: documentTask.appInstanceId, status: DocumentTaskStatus.SUBMITTED }) + // mutationToPublish.source = EntitySourceType.Origin // save to the contract + // if (mode === ModalMode.Forking) { + // mutationToPublish.metadata.fork_of = mutationToPublish.id + // } + // const newAlert = doChecksForAlerts(mutationToPublish, mode === ModalMode.Editing) + // if (newAlert) { + // setAlert(newAlert) + // return + // } + // if (mode === ModalMode.Creating || mode === ModalMode.Forking) { + // try { + // const id = await createMutation( + // mutationToPublish, + // mode === ModalMode.Forking + // ? { askOriginToApplyChanges: isApplyToOriginChecked } + // : undefined + // ) + // switchMutation(id) + // switchPreferredSource(id, EntitySourceType.Origin) + // await deleteLocalMutation(mutationToPublish.id) + // onCloseAll() + // } catch (error: any) { + // if (error?.message === 'Mutation with that ID already exists') { + // setAlert(alerts.idIsNotUnique) + // } + // } + // } else if (mode === ModalMode.Editing) { + // try { + // await editMutation( + // mutationToPublish as MutationDto, + // forkedMutation && isApplyToOriginChecked + // ? forkedMutation.authorId === loggedInAccountId + // ? { applyChangesToOrigin: true } + // : { askOriginToApplyChanges: true } + // : undefined + // ) + // switchPreferredSource(mutationToPublish.id, EntitySourceType.Origin) + // await deleteLocalMutation(mutationToPublish.id) + // onCloseAll() + // } catch (error: any) { + // console.error(error) + // } + // } + } + + const handleChangeModalMode = (itemId: string) => { + setMode(itemId as ModalMode) + } + + return ( + + ) +} diff --git a/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-mutation.tsx b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-mutation.tsx new file mode 100644 index 00000000..0fab9664 --- /dev/null +++ b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-mutation.tsx @@ -0,0 +1,198 @@ +import { EntitySourceType, MutationCreateDto, MutationDto } from '@mweb/backend' +import { + useCreateMutation, + useDeleteLocalMutation, + useEditMutation, + useMutableWeb, +} from '@mweb/engine' +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' +import { cloneDeep } from '../../helpers' +import { useEscape } from '../../hooks/use-escape' +import { AlertProps } from './alert' +import { ModalsConfirm, ModalMode } from './modals-confirm' + +export interface Props { + onCloseCurrent: () => void + onCloseAll: () => void + editingMutation: MutationDto + loggedInAccountId: string +} + +interface IAlert extends AlertProps { + id: string +} + +// ToDo: duplication -- move somewhere +const alerts: { [name: string]: IAlert } = { + noWallet: { + id: 'noWallet', + text: 'Connect the NEAR wallet to create the mutation.', + severity: 'warning', + }, + emptyMutation: { + id: 'emptyMutation', + text: 'A mutation cannot be empty.', + severity: 'warning', + }, + notEditedMutation: { + id: 'notEditedMutation', + text: 'No changes found!', + severity: 'warning', + }, + idIsNotUnique: { + id: 'idIsNotUnique', + text: 'This mutation ID already exists.', + severity: 'warning', + }, + noName: { + id: 'noName', + text: 'Name must be specified.', + severity: 'error', + }, + noImage: { + id: 'noImage', + text: 'Image must be specified.', + severity: 'error', + }, +} + +export const ModalConfirmMutation: FC = ({ + onCloseCurrent, + onCloseAll, + editingMutation, + loggedInAccountId, +}) => { + const { name, image, description, fork_of } = editingMutation.metadata + + // Close modal with escape key + useEscape(onCloseCurrent) // ToDo -- does not work + + const [newName, setName] = useState(name ?? '') + const [newImage, setImage] = useState<{ ipfs_cid?: string } | undefined>(image) + const [newDescription, setDescription] = useState(description ?? '') + const [isApplyToOriginChecked, setIsApplyToOriginChecked] = useState(false) // ToDo: separate checkboxes + const [alert, setAlert] = useState(null) + const { mutations, switchMutation, switchPreferredSource } = useMutableWeb() + + const [mode, setMode] = useState( + !editingMutation.authorId // Newly created local mutation doesn't have author + ? ModalMode.Creating + : editingMutation.authorId === loggedInAccountId + ? ModalMode.Editing + : ModalMode.Forking + ) + + const forkedMutation = useMemo(() => { + if (mode !== ModalMode.Editing || !fork_of) return + return mutations.find((mutation) => mutation.id === fork_of) + }, [fork_of, mutations, mode]) + + const { createMutation, isLoading: isCreating } = useCreateMutation() + const { editMutation, isLoading: isEditing } = useEditMutation() + const { deleteLocalMutation } = useDeleteLocalMutation() + + const isFormDisabled = isCreating || isEditing + + useEffect(() => setAlert(null), [newName, newImage, newDescription, isApplyToOriginChecked]) + + // const checkIfModified = useCallback( + // (mutationToPublish: MutationDto) => + // baseMutation ? !compareMutations(baseMutation, mutationToPublish) : true, + // [baseMutation] + // ) + + const doChecksForAlerts = useCallback( + (mutationToPublish: MutationCreateDto | MutationDto, isEditing: boolean): IAlert | null => { + if (!mutationToPublish.metadata.name) return alerts.noName + if (!mutationToPublish.metadata.image) return alerts.noImage + // if ( + // isEditing && + // !isApplyToOriginChecked && + // !checkIfModified(mutationToPublish as MutationDto) + // ) + // return alerts.notEditedMutation + return null + }, + [newName, newImage, isApplyToOriginChecked] // checkIfModified + ) + + const handleSaveClick = async () => { + const mutationToPublish = cloneDeep(editingMutation) + mutationToPublish.metadata.name = newName.trim() + mutationToPublish.metadata.image = newImage + mutationToPublish.metadata.description = newDescription.trim() + mutationToPublish.source = EntitySourceType.Origin // save to the contract + + if (mode === ModalMode.Forking) { + mutationToPublish.metadata.fork_of = mutationToPublish.id + } + + const newAlert = doChecksForAlerts(mutationToPublish, mode === ModalMode.Editing) + if (newAlert) { + setAlert(newAlert) + return + } + + if (mode === ModalMode.Creating || mode === ModalMode.Forking) { + try { + const id = await createMutation( + mutationToPublish, + mode === ModalMode.Forking + ? { askOriginToApplyChanges: isApplyToOriginChecked } + : undefined + ) + switchMutation(id) + switchPreferredSource(id, EntitySourceType.Origin) + await deleteLocalMutation(mutationToPublish.id) + onCloseAll() + } catch (error: any) { + if (error?.message === 'Mutation with that ID already exists') { + setAlert(alerts.idIsNotUnique) + } + } + } else if (mode === ModalMode.Editing) { + try { + await editMutation( + mutationToPublish as MutationDto, + forkedMutation && isApplyToOriginChecked + ? forkedMutation.authorId === loggedInAccountId + ? { applyChangesToOrigin: true } + : { askOriginToApplyChanges: true } + : undefined + ) + switchPreferredSource(mutationToPublish.id, EntitySourceType.Origin) + await deleteLocalMutation(mutationToPublish.id) + onCloseAll() + } catch (error: any) { + console.error(error) + } + } + } + + const handleChangeModalMode = (itemId: string) => { + setMode(itemId as ModalMode) + } + + return ( + + ) +} diff --git a/apps/extension/src/contentscript/multitable-panel/components/modals-confirm.tsx b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm.tsx index 50db7202..307b2bb9 100644 --- a/apps/extension/src/contentscript/multitable-panel/components/modals-confirm.tsx +++ b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm.tsx @@ -1,26 +1,13 @@ -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' +import { BaseDto } from '@mweb/backend' +import { EntityMetadata } from '@mweb/backend/lib/common/entity-metadata' +import React, { FC } from 'react' import styled from 'styled-components' -import { - useCreateMutation, - useEditMutation, - useMutableWeb, - useDeleteLocalMutation, -} from '@mweb/engine' -import { EntitySourceType, MutationCreateDto, MutationDto } from '@mweb/backend' -import { Image } from './image' -import { useEscape } from '../../hooks/use-escape' import { Alert, AlertProps } from './alert' import { Button } from './button' -import { InputImage } from './upload-image' -import { cloneDeep } from '../../helpers' -import { DropdownButton } from './dropdown-button' import { ButtonsGroup } from './buttons-group' - -enum MutationModalMode { - Editing = 'editing', - Creating = 'creating', - Forking = 'forking', -} +import { DropdownButton } from './dropdown-button' +import { Image } from './image' +import { InputImage } from './upload-image' const ModalConfirmWrapper = styled.div` display: flex; @@ -177,185 +164,68 @@ const CheckboxInput = styled.input` border: 1px solid #384bff; ` -export interface Props { - itemType: 'mutation' | 'document' - onCloseCurrent: () => void - onCloseAll: () => void - editingMutation: MutationDto - loggedInAccountId: string -} - -interface IAlert extends AlertProps { - id: string +export enum ModalMode { + Editing = 'editing', + Creating = 'creating', + Forking = 'forking', } -// ToDo: duplication -- move somewhere -const alerts: { [name: string]: IAlert } = { - noWallet: { - id: 'noWallet', - text: 'Connect the NEAR wallet to create the mutation.', - severity: 'warning', - }, - emptyMutation: { - id: 'emptyMutation', - text: 'A mutation cannot be empty.', - severity: 'warning', - }, - notEditedMutation: { - id: 'notEditedMutation', - text: 'No changes found!', - severity: 'warning', - }, - idIsNotUnique: { - id: 'idIsNotUnique', - text: 'This mutation ID already exists.', - severity: 'warning', - }, - noName: { - id: 'noName', - text: 'Name must be specified.', - severity: 'error', - }, - noImage: { - id: 'noImage', - text: 'Image must be specified.', - severity: 'error', - }, +export type ModalsConfirmProps = { + entityType: string + editingEntity: BaseDto & { metadata: EntityMetadata } + loggedInAccountId: string + mode: ModalMode + alert: AlertProps | null + isFormDisabled: boolean + isApplyToOriginChecked: boolean + newName: string + newImage?: { ipfs_cid?: string } + newDescription: string + forkedEntity?: BaseDto & { metadata: EntityMetadata } + onChangeModalMode: (itemId: string) => void + setIsApplyToOriginChecked: React.Dispatch> + setName: (name: string) => void + setImage: (image: { ipfs_cid?: string }) => void + setDescription: (description: string) => void + onSaveClick: () => void + onCloseCurrent: () => void } -export const ModalConfirm: FC = ({ - itemType, - onCloseCurrent, - onCloseAll, - editingMutation, +export const ModalsConfirm: FC = ({ + entityType, + editingEntity, loggedInAccountId, + mode, + alert, + isFormDisabled, + isApplyToOriginChecked, + newName, + newImage, + newDescription, + forkedEntity, + onChangeModalMode, + setIsApplyToOriginChecked, + setName, + setImage, + setDescription, + onSaveClick, + onCloseCurrent, }) => { - const { name, image, description, fork_of } = editingMutation.metadata - - // Close modal with escape key - useEscape(onCloseCurrent) // ToDo -- does not work - - const [newName, setName] = useState(name ?? '') - const [newImage, setImage] = useState<{ ipfs_cid?: string } | undefined>(image) - const [newDescription, setDescription] = useState(description ?? '') - const [isApplyToOriginChecked, setIsApplyToOriginChecked] = useState(false) // ToDo: separate checkboxes - const [alert, setAlert] = useState(null) - const { mutations, switchMutation, switchPreferredSource } = useMutableWeb() - - const [mode, setMode] = useState( - !editingMutation.authorId // Newly created local mutation doesn't have author - ? MutationModalMode.Creating - : editingMutation.authorId === loggedInAccountId - ? MutationModalMode.Editing - : MutationModalMode.Forking - ) - - const forkedMutation = useMemo(() => { - if (mode !== MutationModalMode.Editing || !fork_of) return null - return mutations.find((mutation) => mutation.id === fork_of) - }, [fork_of, mutations, mode]) - - const { createMutation, isLoading: isCreating } = useCreateMutation() - const { editMutation, isLoading: isEditing } = useEditMutation() - const { deleteLocalMutation } = useDeleteLocalMutation() - - const isFormDisabled = isCreating || isEditing - - useEffect(() => setAlert(null), [newName, newImage, newDescription, isApplyToOriginChecked]) - - // const checkIfModified = useCallback( - // (mutationToPublish: MutationDto) => - // baseMutation ? !compareMutations(baseMutation, mutationToPublish) : true, - // [baseMutation] - // ) - - const doChecksForAlerts = useCallback( - (mutationToPublish: MutationCreateDto | MutationDto, isEditing: boolean): IAlert | null => { - if (!mutationToPublish.metadata.name) return alerts.noName - if (!mutationToPublish.metadata.image) return alerts.noImage - // if ( - // isEditing && - // !isApplyToOriginChecked && - // !checkIfModified(mutationToPublish as MutationDto) - // ) - // return alerts.notEditedMutation - return null - }, - [newName, newImage, isApplyToOriginChecked] // checkIfModified - ) - - const handleSaveClick = async () => { - const mutationToPublish = cloneDeep(editingMutation) - mutationToPublish.metadata.name = newName.trim() - mutationToPublish.metadata.image = newImage - mutationToPublish.metadata.description = newDescription.trim() - mutationToPublish.source = EntitySourceType.Origin // save to the contract - - if (mode === MutationModalMode.Forking) { - mutationToPublish.metadata.fork_of = mutationToPublish.id - } - - const newAlert = doChecksForAlerts(mutationToPublish, mode === MutationModalMode.Editing) - if (newAlert) { - setAlert(newAlert) - return - } - - if (mode === MutationModalMode.Creating || mode === MutationModalMode.Forking) { - try { - const id = await createMutation( - mutationToPublish, - mode === MutationModalMode.Forking - ? { askOriginToApplyChanges: isApplyToOriginChecked } - : undefined - ) - switchMutation(id) - switchPreferredSource(id, EntitySourceType.Origin) - await deleteLocalMutation(mutationToPublish.id) - onCloseAll() - } catch (error: any) { - if (error?.message === 'Mutation with that ID already exists') { - setAlert(alerts.idIsNotUnique) - } - } - } else if (mode === MutationModalMode.Editing) { - try { - await editMutation( - mutationToPublish as MutationDto, - forkedMutation && isApplyToOriginChecked - ? forkedMutation.authorId === loggedInAccountId - ? { applyChangesToOrigin: true } - : { askOriginToApplyChanges: true } - : undefined - ) - switchPreferredSource(mutationToPublish.id, EntitySourceType.Origin) - await deleteLocalMutation(mutationToPublish.id) - onCloseAll() - } catch (error: any) { - console.error(error) - } - } - } - - const handleSaveDropdownChange = (itemId: string) => { - setMode(itemId as MutationModalMode) - } - return ( - {mode === MutationModalMode.Creating - ? `Create your ${itemType}` - : mode === MutationModalMode.Editing - ? `Publish your ${itemType}` - : mode === MutationModalMode.Forking + {mode === ModalMode.Creating + ? `Create your ${entityType}` + : mode === ModalMode.Editing + ? `Publish your ${entityType}` + : mode === ModalMode.Forking ? 'Publish as a fork' : null} {alert ? : null} - {mode === MutationModalMode.Creating ? ( + {mode === ModalMode.Creating ? ( <> @@ -369,7 +239,7 @@ export const ModalConfirm: FC = ({ id={'name'} type={'text'} value={newName} - placeholder={`Enter your ${itemType} name`} + placeholder={`Enter your ${entityType} name`} onChange={(e) => setName(e.target.value)} disabled={isFormDisabled} /> @@ -383,36 +253,36 @@ export const ModalConfirm: FC = ({ setDescription(e.target.value)} disabled={isFormDisabled} /> Description - ) : mode === MutationModalMode.Forking ? ( + ) : mode === ModalMode.Forking ? ( <> {editingMutation.metadata.name} -

{editingMutation.metadata.name}

+

{editingEntity.metadata.name}

by{' '} - {editingMutation.authorId === loggedInAccountId + {editingEntity.authorId === loggedInAccountId ? `me (${loggedInAccountId})` - : editingMutation.authorId} + : editingEntity.authorId}
- {editingMutation.authorId === loggedInAccountId ? null : ( + {editingEntity.authorId === loggedInAccountId ? null : ( Ask Origin to apply changes = ({ id={'name'} type={'text'} value={newName} - placeholder={`Enter your ${itemType} name`} + placeholder={`Enter your ${entityType} name`} onChange={(e) => setName(e.target.value)} disabled={isFormDisabled} /> @@ -449,14 +319,14 @@ export const ModalConfirm: FC = ({ setDescription(e.target.value)} disabled={isFormDisabled} /> Description - ) : mode === MutationModalMode.Editing ? ( + ) : mode === ModalMode.Editing ? ( <> @@ -473,31 +343,31 @@ export const ModalConfirm: FC = ({ - {forkedMutation ? ( + {forkedEntity ? ( <> {forkedMutation.metadata.name} -

{forkedMutation.metadata.name}

+

{forkedEntity.metadata.name}

by{' '} - {forkedMutation.authorId === loggedInAccountId + {forkedEntity.authorId === loggedInAccountId ? `me (${loggedInAccountId})` - : forkedMutation.authorId} + : forkedEntity.authorId}
- {forkedMutation.authorId === loggedInAccountId + {forkedEntity.authorId === loggedInAccountId ? 'Apply changes to Origin' : 'Ask Origin to apply changes'} @@ -515,7 +385,7 @@ export const ModalConfirm: FC = ({ setDescription(e.target.value)} disabled={isFormDisabled} /> @@ -530,23 +400,23 @@ export const ModalConfirm: FC = ({ value={mode} items={[ { - value: MutationModalMode.Forking, + value: ModalMode.Forking, title: 'Fork', - visible: !!editingMutation.authorId, + visible: !!editingEntity.authorId, }, { - value: MutationModalMode.Editing, + value: ModalMode.Editing, title: 'Save', - visible: !!editingMutation.authorId && editingMutation.authorId === loggedInAccountId, + visible: !!editingEntity.authorId && editingEntity.authorId === loggedInAccountId, }, { - value: MutationModalMode.Creating, + value: ModalMode.Creating, title: 'Create', - visible: !editingMutation.authorId, + visible: !editingEntity.authorId, }, ]} - onClick={handleSaveClick} - onChange={handleSaveDropdownChange} + onClick={onSaveClick} + onChange={onChangeModalMode} disabled={isFormDisabled} disabledAll={isFormDisabled} /> diff --git a/apps/extension/src/contentscript/multitable-panel/components/mutation-editor-modal.tsx b/apps/extension/src/contentscript/multitable-panel/components/mutation-editor-modal.tsx index c7e16480..c5998868 100644 --- a/apps/extension/src/contentscript/multitable-panel/components/mutation-editor-modal.tsx +++ b/apps/extension/src/contentscript/multitable-panel/components/mutation-editor-modal.tsx @@ -8,7 +8,7 @@ import { Alert, AlertProps } from './alert' import { ApplicationCardWithDocs, SimpleApplicationCard } from './application-card' import { Button } from './button' import { DocumentsModal } from './documents-modal' -import { ModalConfirm } from './modals-confirm' +import { ModalConfirmMutation } from './modals-confirm-mutation' import { AppInMutation } from '@mweb/backend' import { Image } from './image' import { useSaveMutation, useMutableWeb } from '@mweb/engine' @@ -452,8 +452,7 @@ export const MutationEditorModal: FC = ({ apps, baseMutation, localMutati {isConfirmModalOpen && loggedInAccountId && ( - setIsConfirmModalOpen(false)} onCloseAll={onClose} editingMutation={editingMutation} diff --git a/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx b/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx index 99ce3d59..4f92ac8d 100644 --- a/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx +++ b/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx @@ -1,15 +1,18 @@ +import { EntitySourceType } from '@mweb/backend' +import { useMutableWeb, useDocument } from '@mweb/engine' import { EventEmitter as NEventEmitter } from 'events' -import { useMutableWeb } from '@mweb/engine' +import { useAccountId } from 'near-social-vm' import React, { FC, useEffect, useRef, useState } from 'react' import Draggable from 'react-draggable' import styled from 'styled-components' +import { NearNetworkId } from '../../common/networks' import { getIsPanelUnpinned, removePanelUnpinnedFlag, setPanelUnpinnedFlag } from '../storage' import { PinOutlineIcon, PinSolidIcon } from './assets/vectors' import { Dropdown } from './components/dropdown' import { MutationEditorModal } from './components/mutation-editor-modal' import MutableOverlayContainer from './mutable-overlay-container' -import { NearNetworkId } from '../../common/networks' -import { EntitySourceType } from '@mweb/backend' +import { ModalConfirmDocument } from './components/modals-confirm-document' +import { DocumentTaskStatus } from '@mweb/engine/lib/app/contexts/document-context' const WrapperPanel = styled.div<{ $isAnimated?: boolean }>` // Global Styles @@ -121,6 +124,19 @@ const DragIconWrapper = styled.div` height: 8px; ` +const WhiteBackground = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgb(255 255 255 / 75%); + backdrop-filter: blur(5px); + display: flex; + justify-content: center; + align-items: center; +` + const DragIcon = () => ( @@ -141,6 +157,9 @@ export const MultitablePanel: FC = ({ eventEmitter }) => { const [isNotchDisplayed, setIsNotchDisplayed] = useState(true) const [isModalOpen, setIsModalOpen] = useState(false) const notchRef = useRef(null) + const loggedInAccountId = useAccountId() + const { documentTask, setDocumentTask } = useDocument() + console.log('documentTask', documentTask) useEffect(() => { const timer = setTimeout(() => { @@ -241,6 +260,23 @@ export const MultitablePanel: FC = ({ eventEmitter }) => { )} + + {documentTask?.status === DocumentTaskStatus.RECEIVED && + !!selectedMutation && + !!loggedInAccountId ? ( + + { + setDocumentTask(null) + }} + onCloseAll={() => { + setDocumentTask(null) + }} + /> + + ) : null} ) diff --git a/libs/backend/src/services/document/dtos/document-commit.dto.ts b/libs/backend/src/services/document/dtos/document-commit.dto.ts index fecb98b9..56a4249b 100644 --- a/libs/backend/src/services/document/dtos/document-commit.dto.ts +++ b/libs/backend/src/services/document/dtos/document-commit.dto.ts @@ -1,6 +1,6 @@ import { AppId } from '../../application/application.entity' -import { DocumentMetadata } from '../document.entity' import { BaseDto } from '../../base/base.dto' +import { DocumentMetadata } from '../document.entity' export type DocumentCommitDto = BaseDto & { metadata: DocumentMetadata diff --git a/libs/backend/src/services/document/dtos/document-create.dto.ts b/libs/backend/src/services/document/dtos/document-create.dto.ts index cf168ada..e1cef66b 100644 --- a/libs/backend/src/services/document/dtos/document-create.dto.ts +++ b/libs/backend/src/services/document/dtos/document-create.dto.ts @@ -1,5 +1,5 @@ -import { BaseCreateDto } from '../../base/base-create.dto' import { AppId } from '../../application/application.entity' +import { BaseCreateDto } from '../../base/base-create.dto' import { DocumentMetadata } from '../document.entity' export type DocumentCreateDto = BaseCreateDto & { diff --git a/libs/backend/src/services/document/dtos/document.dto.ts b/libs/backend/src/services/document/dtos/document.dto.ts index aebcb8c9..f53ca5ea 100644 --- a/libs/backend/src/services/document/dtos/document.dto.ts +++ b/libs/backend/src/services/document/dtos/document.dto.ts @@ -1,5 +1,5 @@ -import { BaseDto } from '../../base/base.dto' import { AppId } from '../../application/application.entity' +import { BaseDto } from '../../base/base.dto' import { DocumentMetadata } from '../document.entity' export type DocumentDto = BaseDto & { diff --git a/libs/engine/src/app/app.tsx b/libs/engine/src/app/app.tsx index 5d4c38d7..835b93a8 100644 --- a/libs/engine/src/app/app.tsx +++ b/libs/engine/src/app/app.tsx @@ -12,6 +12,7 @@ import { PickerProvider } from './contexts/picker-context' import { ContextHighlighter } from './components/context-highlighter' import { HighlighterProvider } from './contexts/highlighter-context' import { ModalContextState } from './contexts/modal-context/modal-context' +import { DocumentProvider } from './contexts/document-context' export const App: FC<{ config: EngineConfig @@ -28,24 +29,26 @@ export const App: FC<{ return ( - - - - - - - - - - - {children} - - - + + + + + + + + + + + + {children} + + + + ) diff --git a/libs/engine/src/app/components/context-manager.tsx b/libs/engine/src/app/components/context-manager.tsx index 1fb2cd54..f5cf0718 100644 --- a/libs/engine/src/app/components/context-manager.tsx +++ b/libs/engine/src/app/components/context-manager.tsx @@ -32,6 +32,7 @@ import { useMutableWeb } from '../contexts/mutable-web-context' import { useAppControllers } from '../contexts/mutable-web-context/use-app-controllers' import { useContextApps } from '../contexts/mutable-web-context/use-context-apps' import { useUserLinks } from '../contexts/mutable-web-context/use-user-links' +import { useDocument, DocumentTaskStatus } from '../contexts/document-context' interface WidgetProps { context: TransferableContext @@ -106,6 +107,39 @@ const ContextHandler: FC<{ context: IContextNode; insPoints: InsertionPointWithE const { engine, selectedMutation, refreshMutation, activeApps } = useMutableWeb() const { portals } = useEngine() + const { documentTask, setDocumentTask } = useDocument() + // console.log('documentTask', documentTask) + + const fn = useCallback(async () => { + console.log('documentTask in fn', documentTask) + if (!documentTask) return + if (!selectedMutation) throw new Error('No selected mutation') + const appInstance = selectedMutation.apps.find( + (app) => utils.constructAppInstanceId(app) === documentTask.appInstanceId + ) + if (!appInstance) throw new Error('The app is not active') + const { mutation, document: savedDocument } = + await engine.documentService.commitDocumentToMutation( + selectedMutation.id, + appInstance.appId, + documentTask.document + ) + + // mutation changed + if (mutation) { + // ToDo: workaround to wait when blockchain changes will be propagated + await new Promise((resolve) => setTimeout(resolve, 3000)) + + await refreshMutation(mutation) + } + setDocumentTask(null) + return savedDocument + }, [engine, selectedMutation, refreshMutation, documentTask]) + + useEffect(() => { + if (documentTask?.status === DocumentTaskStatus.SUBMITTED) fn() + }, [documentTask, fn]) + const portalComponents = useMemo(() => { return Array.from(portals.values()) .filter(({ target }) => utils.isTargetMet(target, context)) @@ -280,22 +314,27 @@ const ContextHandler: FC<{ context: IContextNode; insPoints: InsertionPointWithE // ToDo: show fork dialog - const { mutation, document: savedDocument } = - await engine.documentService.commitDocumentToMutation( - selectedMutation.id, - appInstance.appId, - document - ) + if (document.source === 'local') { + const { mutation, document: savedDocument } = + await engine.documentService.commitDocumentToMutation( + selectedMutation.id, + appInstance.appId, + document + ) - // mutation changed - if (mutation) { - // ToDo: workaround to wait when blockchain changes will be propagated - await new Promise((resolve) => setTimeout(resolve, 3000)) + // mutation changed + if (mutation) { + // ToDo: workaround to wait when blockchain changes will be propagated + await new Promise((resolve) => setTimeout(resolve, 3000)) - await refreshMutation(mutation) - } + await refreshMutation(mutation) + } - return savedDocument + return savedDocument + } + setDocumentTask({ document, appInstanceId, status: DocumentTaskStatus.RECEIVED }) + console.log('end') + return document // ToDo: think of it -- not wait for a result } ), [engine, selectedMutation, refreshMutation] diff --git a/libs/engine/src/app/contexts/document-context/document-context.tsx b/libs/engine/src/app/contexts/document-context/document-context.tsx new file mode 100644 index 00000000..92ebe651 --- /dev/null +++ b/libs/engine/src/app/contexts/document-context/document-context.tsx @@ -0,0 +1,27 @@ +import { DocumentCommitDto } from '@mweb/backend' +import { createContext } from 'react' + +export enum DocumentTaskStatus { + RECEIVED, + WAITING, + SUBMITTED, + ERROR, +} + +export type DocumentTask = { + document: DocumentCommitDto + appInstanceId: string + status: DocumentTaskStatus +} + +export type DocumentContextState = { + documentTask: DocumentTask | null + setDocumentTask: (newTask: DocumentTask | null) => void +} + +const contextDefaultValues: DocumentContextState = { + documentTask: null, + setDocumentTask: (newTask: DocumentTask | null) => {}, +} + +export const DocumentContext = createContext(contextDefaultValues) diff --git a/libs/engine/src/app/contexts/document-context/document-provider.tsx b/libs/engine/src/app/contexts/document-context/document-provider.tsx new file mode 100644 index 00000000..8c7d29c3 --- /dev/null +++ b/libs/engine/src/app/contexts/document-context/document-provider.tsx @@ -0,0 +1,18 @@ +import React, { FC, ReactElement, useEffect } from 'react' +import { DocumentContext, DocumentContextState, DocumentTask } from './document-context' + +type Props = { + children?: ReactElement +} + +const DocumentProvider: FC = ({ children }) => { + const [documentTask, setDocumentTask] = React.useState(null) + const state: DocumentContextState = { + documentTask, + setDocumentTask, + } + + return {children} +} + +export { DocumentProvider } diff --git a/libs/engine/src/app/contexts/document-context/index.ts b/libs/engine/src/app/contexts/document-context/index.ts new file mode 100644 index 00000000..de811baf --- /dev/null +++ b/libs/engine/src/app/contexts/document-context/index.ts @@ -0,0 +1,3 @@ +export { DocumentContext, DocumentTaskStatus } from './document-context' +export { DocumentProvider } from './document-provider' +export { useDocument } from './use-document' diff --git a/libs/engine/src/app/contexts/document-context/use-document.ts b/libs/engine/src/app/contexts/document-context/use-document.ts new file mode 100644 index 00000000..27247db9 --- /dev/null +++ b/libs/engine/src/app/contexts/document-context/use-document.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react' +import { DocumentContext } from './document-context' + +export function useDocument() { + return useContext(DocumentContext) +} diff --git a/libs/engine/src/index.ts b/libs/engine/src/index.ts index 8101fb89..2e15bca1 100644 --- a/libs/engine/src/index.ts +++ b/libs/engine/src/index.ts @@ -1,6 +1,7 @@ export * as customElements from './custom-elements' export { App } from './app/app' export { useEngine } from './app/contexts/engine-context' +export { useDocument } from './app/contexts/document-context' export { useMutation, useMutations, diff --git a/libs/shared-components/src/mini-overlay/index.tsx b/libs/shared-components/src/mini-overlay/index.tsx index bb13b30a..afa91ee6 100644 --- a/libs/shared-components/src/mini-overlay/index.tsx +++ b/libs/shared-components/src/mini-overlay/index.tsx @@ -33,12 +33,12 @@ const WrapperDriver = styled.div<{ $isOpen: boolean }>` position: relative; overflow: visible; padding: 0; - width: 58px; .ant-drawer-body { overflow: visible; padding: 0; width: 58px; + direction: rtl; } } ` diff --git a/libs/shared-components/src/mini-overlay/side-panel.tsx b/libs/shared-components/src/mini-overlay/side-panel.tsx index 0c9b3ce7..d22e8196 100644 --- a/libs/shared-components/src/mini-overlay/side-panel.tsx +++ b/libs/shared-components/src/mini-overlay/side-panel.tsx @@ -17,7 +17,7 @@ const SidePanelWrapper = styled.div<{ $isApps: boolean }>` user-select: none; flex-direction: column; justify-content: center; - align-items: center; + align-items: flex-start; border-radius: 4px 0px 0px 4px; background: ${(props) => (props.$isApps ? '#EEEFF5' : '#F8F9FF')}; box-shadow: 0 4px 20px 0 rgba(11, 87, 111, 0.15); @@ -32,6 +32,7 @@ const BadgeWrapper = styled.span` ` const TopBlock = styled.div<{ $open?: boolean; $noMutations: boolean }>` + direction: ltr; display: flex; flex-direction: column; justify-content: center; @@ -151,9 +152,39 @@ const AppsWrapper = styled.div` width: 100%; padding: 5px 6px 5px 7px; gap: 10px; + max-height: calc(100vh - 300px); + overflow-y: auto; + overflow-x: hidden; + + /* width */ + &::-webkit-scrollbar { + width: 5px; + } + + /* Track */ + &::-webkit-scrollbar-track { + box-shadow: inset 0 0 5px grey; + border-radius: 10px; + } + + /* Handle */ + &::-webkit-scrollbar-thumb { + background: #1879ce70; + border-radius: 10px; + } + + /* Handle on hover */ + &::-webkit-scrollbar-thumb:hover { + background: #1879ced8; + } + + & button { + direction: ltr; + } ` const ButtonOpenWrapper = styled.div` + direction: ltr; display: flex; box-sizing: border-box; justify-content: center; @@ -268,7 +299,9 @@ const SidePanel: React.FC = ({ data-mweb-layout-manager="vertical" /> ) : ( - {children} +
+ {children} +
)} Date: Thu, 5 Dec 2024 17:58:39 +0300 Subject: [PATCH 2/2] refactor: replace document task status with reject and resolve callbacks --- .../components/modals-confirm-document.tsx | 15 +--- .../multitable-panel/multitable-panel.tsx | 16 +--- .../src/app/components/context-manager.tsx | 76 ++++++------------- .../document-context/document-context.tsx | 16 ++-- .../document-context/document-provider.tsx | 16 ++++ .../app/contexts/document-context/index.ts | 2 +- 6 files changed, 54 insertions(+), 87 deletions(-) diff --git a/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx index 5001f022..e9f9cc11 100644 --- a/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx +++ b/apps/extension/src/contentscript/multitable-panel/components/modals-confirm-document.tsx @@ -1,25 +1,16 @@ -import { - AppId, - BaseDto, - DocumentMetadata, - EntitySourceType, - MutationCreateDto, - MutationDto, -} from '@mweb/backend' +import { AppId, BaseDto, DocumentMetadata, MutationCreateDto, MutationDto } from '@mweb/backend' import { useAppDocuments, useCreateMutation, useDeleteLocalMutation, useDocument, useEditMutation, - useMutableWeb, } from '@mweb/engine' import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' import { cloneDeep } from '../../helpers' import { useEscape } from '../../hooks/use-escape' import { AlertProps } from './alert' import { ModalsConfirm, ModalMode } from './modals-confirm' -import { DocumentTaskStatus } from '@mweb/engine/lib/app/contexts/document-context' export interface Props { editingDocument: BaseDto & { @@ -104,7 +95,7 @@ export const ModalConfirmDocument: FC = ({ const { createMutation, isLoading: isCreating } = useCreateMutation() const { editMutation, isLoading: isEditing } = useEditMutation() const { deleteLocalMutation } = useDeleteLocalMutation() - const { documentTask, setDocumentTask } = useDocument() + const { documentTask, resolveDocumentTask } = useDocument() const isFormDisabled = isCreating || isEditing @@ -138,7 +129,7 @@ export const ModalConfirmDocument: FC = ({ documentToPublish.metadata.name = newName.trim() documentToPublish.metadata.image = newImage documentToPublish.metadata.description = newDescription.trim() - setDocumentTask({ document: documentToPublish, appInstanceId: documentTask.appInstanceId, status: DocumentTaskStatus.SUBMITTED }) + resolveDocumentTask(documentToPublish) // mutationToPublish.source = EntitySourceType.Origin // save to the contract // if (mode === ModalMode.Forking) { // mutationToPublish.metadata.fork_of = mutationToPublish.id diff --git a/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx b/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx index c4dde488..320c75b0 100644 --- a/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx +++ b/apps/extension/src/contentscript/multitable-panel/multitable-panel.tsx @@ -12,7 +12,6 @@ import { Dropdown } from './components/dropdown' import { MutationEditorModal } from './components/mutation-editor-modal' import MutableOverlayContainer from './mutable-overlay-container' import { ModalConfirmDocument } from './components/modals-confirm-document' -import { DocumentTaskStatus } from '@mweb/engine/lib/app/contexts/document-context' const WrapperPanel = styled.div<{ $isAnimated?: boolean }>` // Global Styles @@ -139,8 +138,7 @@ export const MultitablePanel: FC = ({ eventEmitter }) => { const [isModalOpen, setIsModalOpen] = useState(false) const notchRef = useRef(null) const loggedInAccountId = useAccountId() - const { documentTask, setDocumentTask } = useDocument() - console.log('documentTask', documentTask) + const { documentTask, rejectDocumentTask } = useDocument() useEffect(() => { const timer = setTimeout(() => { @@ -243,19 +241,13 @@ export const MultitablePanel: FC = ({ eventEmitter }) => { )} - {documentTask?.status === DocumentTaskStatus.RECEIVED && - !!selectedMutation && - !!loggedInAccountId ? ( + {documentTask && !!selectedMutation && !!loggedInAccountId ? ( { - setDocumentTask(null) - }} - onCloseAll={() => { - setDocumentTask(null) - }} + onCloseCurrent={rejectDocumentTask} + onCloseAll={rejectDocumentTask} /> ) : null} diff --git a/libs/engine/src/app/components/context-manager.tsx b/libs/engine/src/app/components/context-manager.tsx index f5cf0718..466798fb 100644 --- a/libs/engine/src/app/components/context-manager.tsx +++ b/libs/engine/src/app/components/context-manager.tsx @@ -32,7 +32,7 @@ import { useMutableWeb } from '../contexts/mutable-web-context' import { useAppControllers } from '../contexts/mutable-web-context/use-app-controllers' import { useContextApps } from '../contexts/mutable-web-context/use-context-apps' import { useUserLinks } from '../contexts/mutable-web-context/use-user-links' -import { useDocument, DocumentTaskStatus } from '../contexts/document-context' +import { useDocument } from '../contexts/document-context' interface WidgetProps { context: TransferableContext @@ -104,41 +104,10 @@ const ContextHandler: FC<{ context: IContextNode; insPoints: InsertionPointWithE const { controllers } = useAppControllers(context) const { links, createUserLink, deleteUserLink } = useUserLinks(context) const { apps } = useContextApps(context) - const { engine, selectedMutation, refreshMutation, activeApps } = useMutableWeb() + const { engine, selectedMutation, refreshMutation } = useMutableWeb() const { portals } = useEngine() - const { documentTask, setDocumentTask } = useDocument() - // console.log('documentTask', documentTask) - - const fn = useCallback(async () => { - console.log('documentTask in fn', documentTask) - if (!documentTask) return - if (!selectedMutation) throw new Error('No selected mutation') - const appInstance = selectedMutation.apps.find( - (app) => utils.constructAppInstanceId(app) === documentTask.appInstanceId - ) - if (!appInstance) throw new Error('The app is not active') - const { mutation, document: savedDocument } = - await engine.documentService.commitDocumentToMutation( - selectedMutation.id, - appInstance.appId, - documentTask.document - ) - - // mutation changed - if (mutation) { - // ToDo: workaround to wait when blockchain changes will be propagated - await new Promise((resolve) => setTimeout(resolve, 3000)) - - await refreshMutation(mutation) - } - setDocumentTask(null) - return savedDocument - }, [engine, selectedMutation, refreshMutation, documentTask]) - - useEffect(() => { - if (documentTask?.status === DocumentTaskStatus.SUBMITTED) fn() - }, [documentTask, fn]) + const { setDocumentTask } = useDocument() const portalComponents = useMemo(() => { return Array.from(portals.values()) @@ -312,29 +281,30 @@ const ContextHandler: FC<{ context: IContextNode; insPoints: InsertionPointWithE ) if (!appInstance) throw new Error('The app is not active') - // ToDo: show fork dialog - - if (document.source === 'local') { - const { mutation, document: savedDocument } = - await engine.documentService.commitDocumentToMutation( - selectedMutation.id, - appInstance.appId, - document - ) + // Show fork dialog for documents committing to the origin + // Fork dialog is able to edit a name, description and image + if (document.source === EntitySourceType.Origin) { + document = await new Promise((onResolve, onReject) => + setDocumentTask({ document, appInstanceId, onResolve, onReject }) + ) + } - // mutation changed - if (mutation) { - // ToDo: workaround to wait when blockchain changes will be propagated - await new Promise((resolve) => setTimeout(resolve, 3000)) + const { mutation, document: savedDocument } = + await engine.documentService.commitDocumentToMutation( + selectedMutation.id, + appInstance.appId, + document + ) - await refreshMutation(mutation) - } + // mutation changed + if (mutation) { + // ToDo: workaround to wait when blockchain changes will be propagated + await new Promise((resolve) => setTimeout(resolve, 3000)) - return savedDocument + await refreshMutation(mutation) } - setDocumentTask({ document, appInstanceId, status: DocumentTaskStatus.RECEIVED }) - console.log('end') - return document // ToDo: think of it -- not wait for a result + + return savedDocument } ), [engine, selectedMutation, refreshMutation] diff --git a/libs/engine/src/app/contexts/document-context/document-context.tsx b/libs/engine/src/app/contexts/document-context/document-context.tsx index 92ebe651..82f415d8 100644 --- a/libs/engine/src/app/contexts/document-context/document-context.tsx +++ b/libs/engine/src/app/contexts/document-context/document-context.tsx @@ -1,27 +1,25 @@ import { DocumentCommitDto } from '@mweb/backend' import { createContext } from 'react' -export enum DocumentTaskStatus { - RECEIVED, - WAITING, - SUBMITTED, - ERROR, -} - export type DocumentTask = { document: DocumentCommitDto appInstanceId: string - status: DocumentTaskStatus + onReject: () => void + onResolve: (document: DocumentCommitDto) => void } export type DocumentContextState = { documentTask: DocumentTask | null setDocumentTask: (newTask: DocumentTask | null) => void + rejectDocumentTask: () => void + resolveDocumentTask: (document: DocumentCommitDto) => void } const contextDefaultValues: DocumentContextState = { documentTask: null, - setDocumentTask: (newTask: DocumentTask | null) => {}, + setDocumentTask: () => undefined, + rejectDocumentTask: () => undefined, + resolveDocumentTask: () => undefined, } export const DocumentContext = createContext(contextDefaultValues) diff --git a/libs/engine/src/app/contexts/document-context/document-provider.tsx b/libs/engine/src/app/contexts/document-context/document-provider.tsx index 8c7d29c3..52e77606 100644 --- a/libs/engine/src/app/contexts/document-context/document-provider.tsx +++ b/libs/engine/src/app/contexts/document-context/document-provider.tsx @@ -1,5 +1,6 @@ import React, { FC, ReactElement, useEffect } from 'react' import { DocumentContext, DocumentContextState, DocumentTask } from './document-context' +import { DocumentCommitDto } from '@mweb/backend' type Props = { children?: ReactElement @@ -7,9 +8,24 @@ type Props = { const DocumentProvider: FC = ({ children }) => { const [documentTask, setDocumentTask] = React.useState(null) + + const rejectDocumentTask = () => { + if (!documentTask) return + documentTask.onReject() + setDocumentTask(null) + } + + const commitDocumentTask = (document: DocumentCommitDto) => { + if (!documentTask) return + documentTask.onResolve(document) + setDocumentTask(null) + } + const state: DocumentContextState = { documentTask, setDocumentTask, + resolveDocumentTask: commitDocumentTask, + rejectDocumentTask, } return {children} diff --git a/libs/engine/src/app/contexts/document-context/index.ts b/libs/engine/src/app/contexts/document-context/index.ts index de811baf..34bbd4c3 100644 --- a/libs/engine/src/app/contexts/document-context/index.ts +++ b/libs/engine/src/app/contexts/document-context/index.ts @@ -1,3 +1,3 @@ -export { DocumentContext, DocumentTaskStatus } from './document-context' +export { DocumentContext } from './document-context' export { DocumentProvider } from './document-provider' export { useDocument } from './use-document'