Add ability to accept chat
This commit is contained in:
parent
1ed1f3fd2e
commit
8492cc59e2
|
@ -0,0 +1,24 @@
|
||||||
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
import type { IChat } from 'soapbox/queries/chats';
|
||||||
|
|
||||||
|
const ChatContext = createContext<any>({
|
||||||
|
chat: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ChatProvider: React.FC = ({ children }) => {
|
||||||
|
const [chat, setChat] = useState<IChat>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatContext.Provider value={{ chat, setChat }}>{children}</ChatContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IChatContext {
|
||||||
|
chat: IChat | null
|
||||||
|
setChat: React.Dispatch<React.SetStateAction<IChat | null>>
|
||||||
|
}
|
||||||
|
|
||||||
|
const useChatContext = (): IChatContext => useContext(ChatContext);
|
||||||
|
|
||||||
|
export { ChatContext, ChatProvider, useChatContext };
|
|
@ -8,7 +8,7 @@ import {
|
||||||
markChatRead,
|
markChatRead,
|
||||||
} from 'soapbox/actions/chats';
|
} from 'soapbox/actions/chats';
|
||||||
import { uploadMedia } from 'soapbox/actions/media';
|
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 UploadProgress from 'soapbox/components/upload-progress';
|
||||||
import UploadButton from 'soapbox/features/compose/components/upload_button';
|
import UploadButton from 'soapbox/features/compose/components/upload_button';
|
||||||
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
||||||
|
@ -41,7 +41,7 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize }) => {
|
||||||
const chatMessageIds = useAppSelector(state => state.chat_message_lists.get(chat.id, ImmutableOrderedSet<string>()));
|
const chatMessageIds = useAppSelector(state => state.chat_message_lists.get(chat.id, ImmutableOrderedSet<string>()));
|
||||||
const account = useOwnAccount();
|
const account = useOwnAccount();
|
||||||
|
|
||||||
const { createChatMessage, markChatAsRead } = useChat(chat.id);
|
const { createChatMessage, markChatAsRead, acceptChat } = useChat(chat.id);
|
||||||
|
|
||||||
const [content, setContent] = useState<string>('');
|
const [content, setContent] = useState<string>('');
|
||||||
const [attachment, setAttachment] = useState<any>(undefined);
|
const [attachment, setAttachment] = useState<any>(undefined);
|
||||||
|
@ -51,6 +51,8 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize }) => {
|
||||||
|
|
||||||
const inputElem = useRef<HTMLTextAreaElement | null>(null);
|
const inputElem = useRef<HTMLTextAreaElement | null>(null);
|
||||||
|
|
||||||
|
const needsAcceptance = !chat.accepted && chat.created_by_account !== account?.id;
|
||||||
|
|
||||||
const isSubmitDisabled = content.length === 0 && !attachment;
|
const isSubmitDisabled = content.length === 0 && !attachment;
|
||||||
|
|
||||||
// TODO: needs last_read_id param
|
// TODO: needs last_read_id param
|
||||||
|
@ -216,8 +218,37 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className='overflow-hidden flex flex-grow' onMouseOver={handleMouseOver}>
|
<Stack className='overflow-hidden flex flex-grow' onMouseOver={handleMouseOver}>
|
||||||
<div className='flex-grow h-full overflow-hidden'>
|
<div className='flex-grow h-full overflow-hidden flex justify-center'>
|
||||||
<ChatMessageList chatMessageIds={chatMessageIds} chat={chat} autosize />
|
{needsAcceptance ? (
|
||||||
|
<Stack justifyContent='center' alignItems='center' space={5} className='w-3/4 mx-auto'>
|
||||||
|
<Stack alignItems='center' space={2}>
|
||||||
|
<Avatar src={chat.account.avatar_static} size={75} />
|
||||||
|
<Text size='lg' align='center'>
|
||||||
|
<Text tag='span' weight='semibold'>@{chat.account.acct}</Text>
|
||||||
|
{' '}
|
||||||
|
<Text tag='span'>wants to start a chat with you</Text>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack space={2} className='w-full'>
|
||||||
|
<Button
|
||||||
|
theme='primary'
|
||||||
|
block
|
||||||
|
onClick={() => acceptChat.mutate()}
|
||||||
|
disabled={acceptChat.isLoading}
|
||||||
|
>
|
||||||
|
Accept
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<HStack alignItems='center' space={2} className='w-full'>
|
||||||
|
<Button theme='accent' block>Leave chat</Button>
|
||||||
|
<Button theme='secondary' block>Report</Button>
|
||||||
|
</HStack>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
|
<ChatMessageList chatMessageIds={chatMessageIds} chat={chat} autosize />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-auto p-4 shadow-3xl'>
|
<div className='mt-auto p-4 shadow-3xl'>
|
||||||
|
|
|
@ -275,7 +275,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isMyMessage ? (
|
{isMyMessage ? (
|
||||||
<div className='hidden group-hover:block mr-2 text-gray-500'>
|
<div className='hidden focus:block group-hover:block mr-2 text-gray-500'>
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
items={menu}
|
items={menu}
|
||||||
src={require('@tabler/icons/dots.svg')}
|
src={require('@tabler/icons/dots.svg')}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import snackbar from 'soapbox/actions/snackbar';
|
||||||
import AccountSearch from 'soapbox/components/account_search';
|
import AccountSearch from 'soapbox/components/account_search';
|
||||||
import { Avatar, Button, Counter, HStack, Icon, IconButton, Input, Spinner, Stack, Text } from 'soapbox/components/ui';
|
import { Avatar, Button, Counter, HStack, Icon, IconButton, Input, Spinner, Stack, Text } from 'soapbox/components/ui';
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
|
import { ChatProvider, useChatContext } from 'soapbox/contexts/chat-context';
|
||||||
import AudioToggle from 'soapbox/features/chats/components/audio-toggle';
|
import AudioToggle from 'soapbox/features/chats/components/audio-toggle';
|
||||||
import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account';
|
import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account';
|
||||||
import { useAppDispatch, useAppSelector, useDebounce, useSettings } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector, useDebounce, useSettings } from 'soapbox/hooks';
|
||||||
|
@ -47,12 +48,13 @@ const makeNormalizeChatPanes = () => createSelector([
|
||||||
|
|
||||||
const normalizeChatPanes = makeNormalizeChatPanes();
|
const normalizeChatPanes = makeNormalizeChatPanes();
|
||||||
|
|
||||||
const ChatPanes = () => {
|
const ChatPane = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const debounce = useDebounce;
|
const debounce = useDebounce;
|
||||||
|
|
||||||
const [chat, setChat] = useState<IChat | null>();
|
const { chat, setChat } = useChatContext();
|
||||||
|
|
||||||
const [value, setValue] = useState<string>();
|
const [value, setValue] = useState<string>();
|
||||||
const debouncedValue = debounce(value as string, 300);
|
const debouncedValue = debounce(value as string, 300);
|
||||||
|
|
||||||
|
@ -210,4 +212,4 @@ const ChatPanes = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ChatPanes;
|
export default ChatPane;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ChatProvider } from 'soapbox/contexts/chat-context';
|
||||||
|
|
||||||
|
import ChatPane from './chat-pane';
|
||||||
|
|
||||||
|
const ChatWidget = () => {
|
||||||
|
return (
|
||||||
|
<ChatProvider>
|
||||||
|
<ChatPane />
|
||||||
|
</ChatProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatWidget;
|
|
@ -89,7 +89,7 @@ import {
|
||||||
MfaForm,
|
MfaForm,
|
||||||
ChatIndex,
|
ChatIndex,
|
||||||
ChatRoom,
|
ChatRoom,
|
||||||
ChatPanes,
|
ChatWidget,
|
||||||
ServerInfo,
|
ServerInfo,
|
||||||
Dashboard,
|
Dashboard,
|
||||||
ModerationLog,
|
ModerationLog,
|
||||||
|
@ -680,7 +680,7 @@ const UI: React.FC = ({ children }) => {
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
{me && features.chats && !mobile && (
|
{me && features.chats && !mobile && (
|
||||||
<BundleContainer fetchComponent={ChatPanes}>
|
<BundleContainer fetchComponent={ChatWidget}>
|
||||||
{Component => <Component />}
|
{Component => <Component />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -318,8 +318,8 @@ export function ChatRoom() {
|
||||||
return import(/* webpackChunkName: "features/chats/chat_room" */'../../chats/chat-room');
|
return import(/* webpackChunkName: "features/chats/chat_room" */'../../chats/chat-room');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatPanes() {
|
export function ChatWidget() {
|
||||||
return import(/* webpackChunkName: "features/chats/components/chat_panes" */'../../chats/components/chat-panes');
|
return import(/* webpackChunkName: "features/chats/components/chat-widget" */'../../chats/components/chat-widget');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ServerInfo() {
|
export function ServerInfo() {
|
||||||
|
|
|
@ -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 { useApi } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import { queryClient } from './client';
|
||||||
|
|
||||||
export interface IChat {
|
export interface IChat {
|
||||||
id: string
|
id: string
|
||||||
unread: number
|
unread: number
|
||||||
created_by_account: number
|
created_by_account: string
|
||||||
last_message: null | string
|
last_message: null | string
|
||||||
created_at: Date
|
created_at: Date
|
||||||
updated_at: Date
|
updated_at: Date
|
||||||
|
@ -90,6 +93,7 @@ const useChats = () => {
|
||||||
|
|
||||||
const useChat = (chatId: string) => {
|
const useChat = (chatId: string) => {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
const { setChat } = useChatContext();
|
||||||
|
|
||||||
const markChatAsRead = () => api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/read`);
|
const markChatAsRead = () => api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/read`);
|
||||||
|
|
||||||
|
@ -99,7 +103,15 @@ const useChat = (chatId: string) => {
|
||||||
|
|
||||||
const deleteChatMessage = (chatMessageId: string) => api.delete<IChat>(`/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}`);
|
const deleteChatMessage = (chatMessageId: string) => api.delete<IChat>(`/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}`);
|
||||||
|
|
||||||
return { createChatMessage, markChatAsRead, deleteChatMessage };
|
const acceptChat = useMutation(() => api.post<IChat>(`/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 };
|
export { useChat, useChats, useChatMessages };
|
||||||
|
|
Loading…
Reference in New Issue