diff --git a/web/chat/edit-message-provider.js b/web/chat/edit-message-provider.js index 139d9e7f9..134e90a52 100644 --- a/web/chat/edit-message-provider.js +++ b/web/chat/edit-message-provider.js @@ -1,97 +1,110 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import ModalOverlay from 'lib/components/modal-overlay.react.js'; import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js'; import type { ThreadInfo } from 'lib/types/thread-types'; export type EditState = { +messageInfo: ChatMessageInfoItem, +threadInfo: ThreadInfo, +editedMessageDraft: ?string, +isError: boolean, }; type EditModalContextType = { +renderEditModal: (params: EditState) => void, +clearEditModal: () => void, +editState: ?EditState, +setDraft: string => void, + +setError: boolean => void, }; const EditModalContext: React.Context = React.createContext({ renderEditModal: () => {}, clearEditModal: () => {}, editState: null, setDraft: () => {}, + setError: () => {}, }); type Props = { +children: React.Node, }; function EditModalProvider(props: Props): React.Node { const { children } = props; const [editState, setEditState] = React.useState(null); const clearEditModal = React.useCallback(() => { setEditState(null); }, []); const renderEditModal = React.useCallback((newEditState: EditState): void => { setEditState(newEditState); }, []); const modal = React.useMemo(() => { // TODO: Add modal return null; }, []); const setDraft = React.useCallback( (draft: ?string) => { if (!editState) { return; } setEditState({ ...editState, editedMessageDraft: draft, }); }, [editState, setEditState], ); + const setError = React.useCallback( + (isError: boolean) => { + invariant(editState, 'editState should be set in setError'); + setEditState({ + ...editState, + isError, + }); + }, + [editState, setEditState], + ); const value = React.useMemo( () => ({ renderEditModal, clearEditModal: clearEditModal, editState, setDraft, + setError, }), - [renderEditModal, clearEditModal, editState, setDraft], + [renderEditModal, clearEditModal, editState, setDraft, setError], ); const modalOverlay = React.useMemo(() => { if (!modal) { return null; } return {modal}; }, [clearEditModal, modal]); return ( {children} {modalOverlay} ); } function useEditModalContext(): EditModalContextType { const context = React.useContext(EditModalContext); invariant(context, 'EditModalContext not found'); return context; } export { EditModalProvider, useEditModalContext }; diff --git a/web/chat/edit-text-message.react.js b/web/chat/edit-text-message.react.js index 765c35fc6..329aa19c8 100644 --- a/web/chat/edit-text-message.react.js +++ b/web/chat/edit-text-message.react.js @@ -1,103 +1,139 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import { XCircle as XCircleIcon } from 'react-feather'; import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js'; +import { useEditMessage } from 'lib/shared/edit-messages-utils.js'; +import { trimMessage } from 'lib/shared/message-utils.js'; import { type ThreadInfo } from 'lib/types/thread-types.js'; import cssInputBar from './chat-input-bar.css'; import ChatInputTextArea from './chat-input-text-area.react.js'; import ComposedMessage from './composed-message.react.js'; import { useEditModalContext } from './edit-message-provider.js'; import css from './edit-text-message.css'; import type { ButtonColor } from '../components/button.react.js'; import Button from '../components/button.react.js'; type Props = { +item: ChatMessageInfoItem, +threadInfo: ThreadInfo, +background: boolean, }; const cancelButtonColor: ButtonColor = { backgroundColor: 'transparent', }; function EditTextMessage(props: Props): React.Node { - const { background, threadInfo } = props; - const { editState, clearEditModal, setDraft } = useEditModalContext(); + const { background, threadInfo, item } = props; + const { editState, clearEditModal, setDraft, setError } = + useEditModalContext(); + const editMessage = useEditMessage(); const editedMessageDraft = editState?.editedMessageDraft ?? ''; const threadColor = threadInfo.color; const saveButtonColor: ButtonColor = React.useMemo( () => ({ backgroundColor: `#${threadColor}`, }), [threadColor], ); + + const isMessageEdited = React.useMemo(() => { + const { messageInfo } = item; + if (!messageInfo || !messageInfo.text || !editState) { + return false; + } + if (!editedMessageDraft) { + return false; + } + const trimmedDraft = trimMessage(editedMessageDraft); + return trimmedDraft !== messageInfo.text; + }, [editState, editedMessageDraft, item]); + + const checkAndEdit = async () => { + const { id: messageInfoID } = item.messageInfo; + if (!isMessageEdited) { + clearEditModal(); + return; + } + if (!messageInfoID || !editState?.editedMessageDraft) { + return; + } + try { + await editMessage(messageInfoID, editState.editedMessageDraft); + clearEditModal(); + } catch (e) { + setError(true); + } + }; + let editFailed; if (editState?.isError) { editFailed = (
Edit failed.
Please try again.
); } const containerStyle = classNames(css.editMessage, { [css.backgroundEditMessage]: background, }); return (
{editFailed}
); } const ComposedEditTextMessage: React.ComponentType = React.memo( function ComposedEditTextMessage(props) { const { background, ...restProps } = props; return ( ); }, ); export { EditTextMessage, ComposedEditTextMessage };