diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d59de0a4..6a2d2df3 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,11 @@ # @baseapp-frontend/components +## 1.0.26 + +### Patch Changes + +- Use optimistic update when sending message + ## 1.0.25 ### Patch Changes diff --git a/packages/components/modules/messages/common/graphql/mutations/SendMessage.ts b/packages/components/modules/messages/common/graphql/mutations/SendMessage.ts index acb8db34..5c7e2677 100644 --- a/packages/components/modules/messages/common/graphql/mutations/SendMessage.ts +++ b/packages/components/modules/messages/common/graphql/mutations/SendMessage.ts @@ -5,11 +5,13 @@ import { Disposable, UseMutationConfig, graphql, useMutation } from 'react-relay import { SendMessageMutation } from '../../../../../__generated__/SendMessageMutation.graphql' export const SendMessageMutationQuery = graphql` - mutation SendMessageMutation($input: ChatRoomSendMessageInput!, $connections: [ID!]!) { + mutation SendMessageMutation($input: ChatRoomSendMessageInput!, $connections: [ID!]!) + @raw_response_type { chatRoomSendMessage(input: $input) { message @prependEdge(connections: $connections) { node { id + messageType ...MessageItemFragment } } diff --git a/packages/components/modules/messages/common/graphql/subscriptions/useMessagesListSubscription.tsx b/packages/components/modules/messages/common/graphql/subscriptions/useMessagesListSubscription.tsx index 20d81ff8..bff82a7b 100644 --- a/packages/components/modules/messages/common/graphql/subscriptions/useMessagesListSubscription.tsx +++ b/packages/components/modules/messages/common/graphql/subscriptions/useMessagesListSubscription.tsx @@ -3,8 +3,8 @@ import { useMemo } from 'react' import { ConnectionHandler, graphql, useSubscription } from 'react-relay' export const newMessageSubscription = graphql` - subscription useMessagesListSubscription($roomId: ID!, $connections: [ID!]!) { - chatRoomOnMessage(roomId: $roomId) { + subscription useMessagesListSubscription($roomId: ID!, $profileId: ID!, $connections: [ID!]!) { + chatRoomOnMessage(roomId: $roomId, profileId: $profileId) { message @prependEdge(connections: $connections) { node { ...MessageItemFragment @@ -17,13 +17,14 @@ export const newMessageSubscription = graphql` } ` -export const useMessagesListSubscription = (roomId: string) => { +export const useMessagesListSubscription = (roomId: string, profileId: string) => { const config = useMemo(() => { const connectionID = ConnectionHandler.getConnectionID(roomId, 'chatRoom_allMessages') return { subscription: newMessageSubscription, variables: { roomId, + profileId, connections: [connectionID], }, onError: console.error, diff --git a/packages/components/modules/messages/web/MessagesList/index.tsx b/packages/components/modules/messages/web/MessagesList/index.tsx index a2843758..982bdf1f 100644 --- a/packages/components/modules/messages/web/MessagesList/index.tsx +++ b/packages/components/modules/messages/web/MessagesList/index.tsx @@ -79,7 +79,8 @@ const MessagesList: FC = ({ } }, [room?.id, room?.unreadMessages?.count, currentProfile]) - useMessagesListSubscription(room?.id) + useMessagesListSubscription(room.id, currentProfile?.id!) + // TODO: Is there a safer way to ensure the current profile id is not undefined? const renderLoadingState = () => { if (!isLoadingNext) return diff --git a/packages/components/modules/messages/web/SendMessage/index.tsx b/packages/components/modules/messages/web/SendMessage/index.tsx index 15a9214f..74b91605 100644 --- a/packages/components/modules/messages/web/SendMessage/index.tsx +++ b/packages/components/modules/messages/web/SendMessage/index.tsx @@ -3,7 +3,7 @@ import { forwardRef } from 'react' import { useCurrentProfile } from '@baseapp-frontend/authentication' -import { setFormRelayErrors } from '@baseapp-frontend/utils' +import { setFormRelayErrors, useNotification } from '@baseapp-frontend/utils' import { zodResolver } from '@hookform/resolvers/zod' import { useForm } from 'react-hook-form' @@ -15,7 +15,7 @@ import { SocialUpsertForm, } from '../../../__shared__/common' import { SocialInput as DefaultSocialInput } from '../../../__shared__/web' -import { useSendMessageMutation } from '../../common' +import { MESSAGE_TYPE, useSendMessageMutation } from '../../common' import { SendMessageProps } from './types' let nextClientMutationId = 0 @@ -76,6 +76,7 @@ let nextClientMutationId = 0 const SendMessage = forwardRef( ({ roomId, SocialInput = DefaultSocialInput, SocialInputProps = {} }, ref) => { const { currentProfile } = useCurrentProfile() + const { sendToast } = useNotification() const form = useForm({ defaultValues: DEFAULT_SOCIAL_UPSERT_FORM_VALUES, @@ -90,32 +91,57 @@ const SendMessage = forwardRef( const clientMutationId = nextClientMutationId.toString() const connectionID = ConnectionHandler.getConnectionID(roomId, 'chatRoom_allMessages') + const content = data.body commitMutation({ variables: { input: { - content: data.body, + content, profileId: currentProfile?.id, roomId, clientMutationId, }, connections: [connectionID], }, + optimisticResponse: { + chatRoomSendMessage: { + message: { + node: { + id: `client:new_message:${Date.now()}`, + content, + created: new Date(Date.now()).toISOString(), + deleted: false, + extraData: null, + messageType: MESSAGE_TYPE.user, + inReplyTo: null, + isRead: true, + pk: 0, // This property is required, so we need to provide something to keep typescript happy + profile: { + id: currentProfile.id, + }, + verb: 'SENT_MESSAGE', + }, + }, + errors: [], + }, + }, onCompleted: (response, errors) => { if (errors) { // TODO: handle errors - console.error(errors) + sendToast('Your last message could not be sent. Please try again.', { type: 'error' }) } const mutationErrors = response?.chatRoomSendMessage?.errors - setFormRelayErrors(form, mutationErrors) - - if (!mutationErrors?.length) { - form.reset() + if (mutationErrors) { + setFormRelayErrors(form, mutationErrors) + sendToast('Your last message could not be sent. Please try again.', { type: 'error' }) } }, // TODO: handle errors - onError: console.error, + onError: () => { + sendToast('Your last message could not be sent. Please try again.', { type: 'error' }) + }, }) + form.reset() } return ( diff --git a/packages/components/package.json b/packages/components/package.json index 1b550633..bdf6c639 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/components", "description": "BaseApp components modules such as comments, notifications, messages, and more.", - "version": "1.0.25", + "version": "1.0.26", "sideEffects": false, "scripts": { "babel:transpile": "babel modules -d tmp-babel --extensions .ts,.tsx --ignore '**/__tests__/**','**/__storybook__/**'", diff --git a/packages/components/schema.graphql b/packages/components/schema.graphql index 99edce81..ec5740fc 100644 --- a/packages/components/schema.graphql +++ b/packages/components/schema.graphql @@ -1679,7 +1679,7 @@ type RoleUpdatePayload { } type Subscription { - chatRoomOnMessage(roomId: ID!): ChatRoomOnMessage + chatRoomOnMessage(profileId: ID!, roomId: ID!): ChatRoomOnMessage chatRoomOnRoomUpdate(profileId: ID!): ChatRoomOnRoomUpdate chatRoomOnMessagesCountUpdate(profileId: ID!): ChatRoomOnMessagesCountUpdate onNotificationChange: OnNotificationChange