From 8492cc59e270aa4ce486e3f679bea55cf370debc Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 16 Aug 2022 13:38:17 -0400 Subject: [PATCH] Add ability to accept chat --- app/soapbox/contexts/chat-context.tsx | 24 ++++++++++++ .../features/chats/components/chat-box.tsx | 39 +++++++++++++++++-- .../chats/components/chat-message-list.tsx | 2 +- .../{chat-panes.tsx => chat-pane.tsx} | 8 ++-- .../features/chats/components/chat-widget.tsx | 15 +++++++ app/soapbox/features/ui/index.tsx | 4 +- .../features/ui/util/async-components.ts | 4 +- app/soapbox/queries/chats.ts | 18 +++++++-- 8 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 app/soapbox/contexts/chat-context.tsx rename app/soapbox/features/chats/components/{chat-panes.tsx => chat-pane.tsx} (97%) create mode 100644 app/soapbox/features/chats/components/chat-widget.tsx diff --git a/app/soapbox/contexts/chat-context.tsx b/app/soapbox/contexts/chat-context.tsx new file mode 100644 index 000000000..c005d9eea --- /dev/null +++ b/app/soapbox/contexts/chat-context.tsx @@ -0,0 +1,24 @@ +import React, { createContext, useContext, useState } from 'react'; + +import type { IChat } from 'soapbox/queries/chats'; + +const ChatContext = createContext({ + chat: null, +}); + +const ChatProvider: React.FC = ({ children }) => { + const [chat, setChat] = useState(); + + return ( + {children} + ); +}; + +interface IChatContext { + chat: IChat | null + setChat: React.Dispatch> +} + +const useChatContext = (): IChatContext => useContext(ChatContext); + +export { ChatContext, ChatProvider, useChatContext }; diff --git a/app/soapbox/features/chats/components/chat-box.tsx b/app/soapbox/features/chats/components/chat-box.tsx index 836f2169e..2c7b4585d 100644 --- a/app/soapbox/features/chats/components/chat-box.tsx +++ b/app/soapbox/features/chats/components/chat-box.tsx @@ -8,7 +8,7 @@ import { markChatRead, } from 'soapbox/actions/chats'; import { uploadMedia } from 'soapbox/actions/media'; -import { HStack, Icon, IconButton, Input, Stack, Textarea } from 'soapbox/components/ui'; +import { Avatar, Button, HStack, Icon, IconButton, Input, Stack, Text, Textarea } from 'soapbox/components/ui'; import UploadProgress from 'soapbox/components/upload-progress'; import UploadButton from 'soapbox/features/compose/components/upload_button'; import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks'; @@ -41,7 +41,7 @@ const ChatBox: React.FC = ({ chat, onSetInputRef, autosize }) => { const chatMessageIds = useAppSelector(state => state.chat_message_lists.get(chat.id, ImmutableOrderedSet())); const account = useOwnAccount(); - const { createChatMessage, markChatAsRead } = useChat(chat.id); + const { createChatMessage, markChatAsRead, acceptChat } = useChat(chat.id); const [content, setContent] = useState(''); const [attachment, setAttachment] = useState(undefined); @@ -51,6 +51,8 @@ const ChatBox: React.FC = ({ chat, onSetInputRef, autosize }) => { const inputElem = useRef(null); + const needsAcceptance = !chat.accepted && chat.created_by_account !== account?.id; + const isSubmitDisabled = content.length === 0 && !attachment; // TODO: needs last_read_id param @@ -216,8 +218,37 @@ const ChatBox: React.FC = ({ chat, onSetInputRef, autosize }) => { return ( -
- +
+ {needsAcceptance ? ( + + + + + @{chat.account.acct} + {' '} + wants to start a chat with you + + + + + + + + + + + + + ) : ( + + )}
diff --git a/app/soapbox/features/chats/components/chat-message-list.tsx b/app/soapbox/features/chats/components/chat-message-list.tsx index 50026c4d1..0ee6bbff8 100644 --- a/app/soapbox/features/chats/components/chat-message-list.tsx +++ b/app/soapbox/features/chats/components/chat-message-list.tsx @@ -275,7 +275,7 @@ const ChatMessageList: React.FC = ({ chat, chatMessageIds, aut })} > {isMyMessage ? ( -
+
createSelector([ const normalizeChatPanes = makeNormalizeChatPanes(); -const ChatPanes = () => { +const ChatPane = () => { const intl = useIntl(); const dispatch = useAppDispatch(); const debounce = useDebounce; - const [chat, setChat] = useState(); + const { chat, setChat } = useChatContext(); + const [value, setValue] = useState(); const debouncedValue = debounce(value as string, 300); @@ -210,4 +212,4 @@ const ChatPanes = () => { ); }; -export default ChatPanes; +export default ChatPane; diff --git a/app/soapbox/features/chats/components/chat-widget.tsx b/app/soapbox/features/chats/components/chat-widget.tsx new file mode 100644 index 000000000..3fa52641f --- /dev/null +++ b/app/soapbox/features/chats/components/chat-widget.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { ChatProvider } from 'soapbox/contexts/chat-context'; + +import ChatPane from './chat-pane'; + +const ChatWidget = () => { + return ( + + + + ); +}; + +export default ChatWidget; diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 82fb9308e..b136ebdb8 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -89,7 +89,7 @@ import { MfaForm, ChatIndex, ChatRoom, - ChatPanes, + ChatWidget, ServerInfo, Dashboard, ModerationLog, @@ -680,7 +680,7 @@ const UI: React.FC = ({ children }) => { )} {me && features.chats && !mobile && ( - + {Component => } )} diff --git a/app/soapbox/features/ui/util/async-components.ts b/app/soapbox/features/ui/util/async-components.ts index e73efd243..39dd61b9d 100644 --- a/app/soapbox/features/ui/util/async-components.ts +++ b/app/soapbox/features/ui/util/async-components.ts @@ -318,8 +318,8 @@ export function ChatRoom() { return import(/* webpackChunkName: "features/chats/chat_room" */'../../chats/chat-room'); } -export function ChatPanes() { - return import(/* webpackChunkName: "features/chats/components/chat_panes" */'../../chats/components/chat-panes'); +export function ChatWidget() { + return import(/* webpackChunkName: "features/chats/components/chat-widget" */'../../chats/components/chat-widget'); } export function ServerInfo() { diff --git a/app/soapbox/queries/chats.ts b/app/soapbox/queries/chats.ts index bef239a82..a16f593af 100644 --- a/app/soapbox/queries/chats.ts +++ b/app/soapbox/queries/chats.ts @@ -1,11 +1,14 @@ -import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; +import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query'; +import { useChatContext } from 'soapbox/contexts/chat-context'; import { useApi } from 'soapbox/hooks'; +import { queryClient } from './client'; + export interface IChat { id: string unread: number - created_by_account: number + created_by_account: string last_message: null | string created_at: Date updated_at: Date @@ -90,6 +93,7 @@ const useChats = () => { const useChat = (chatId: string) => { const api = useApi(); + const { setChat } = useChatContext(); const markChatAsRead = () => api.post(`/api/v1/pleroma/chats/${chatId}/read`); @@ -99,7 +103,15 @@ const useChat = (chatId: string) => { const deleteChatMessage = (chatMessageId: string) => api.delete(`/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}`); - return { createChatMessage, markChatAsRead, deleteChatMessage }; + const acceptChat = useMutation(() => api.post(`/api/v1/pleroma/chats/${chatId}/accept`), { + onSuccess(response) { + setChat(response.data); + queryClient.invalidateQueries(['chats', 'messages', chatId]); + queryClient.invalidateQueries(['chats']); + }, + }); + + return { createChatMessage, markChatAsRead, deleteChatMessage, acceptChat }; }; export { useChat, useChats, useChatMessages };