pl-fe: add shoutbox list item to chats page
Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { verifyCredentials } from './auth';
|
||||
import { importEntities } from './importer';
|
||||
import { getMeToken, getMeUrl } from './me';
|
||||
|
||||
import type { PlApiClient, ShoutMessage } from 'pl-api';
|
||||
@ -8,17 +9,25 @@ const SHOUTBOX_MESSAGE_IMPORT = 'SHOUTBOX_MESSAGE_IMPORT' as const;
|
||||
const SHOUTBOX_MESSAGES_IMPORT = 'SHOUTBOX_MESSAGES_IMPORT' as const;
|
||||
const SHOUTBOX_CONNECT = 'SHOUTBOX_CONNECT' as const;
|
||||
|
||||
const importShoutboxMessages = (messages: ShoutMessage[]) => ({
|
||||
type: SHOUTBOX_MESSAGES_IMPORT,
|
||||
messages,
|
||||
});
|
||||
const importShoutboxMessages = (messages: ShoutMessage[]) => (dispatch: AppDispatch): ShoutboxAction => {
|
||||
dispatch(importEntities({ accounts: messages.map((message) => message.author) }));
|
||||
|
||||
const importShoutboxMessage = (message: ShoutMessage) => ({
|
||||
type: SHOUTBOX_MESSAGE_IMPORT,
|
||||
message,
|
||||
});
|
||||
return dispatch({
|
||||
type: SHOUTBOX_MESSAGES_IMPORT,
|
||||
messages,
|
||||
});
|
||||
};
|
||||
|
||||
const connectShoutbox = (dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const importShoutboxMessage = (message: ShoutMessage) => (dispatch: AppDispatch): ShoutboxAction => {
|
||||
dispatch(importEntities({ accounts: [message.author] }));
|
||||
|
||||
return dispatch({
|
||||
type: SHOUTBOX_MESSAGE_IMPORT,
|
||||
message,
|
||||
});
|
||||
};
|
||||
|
||||
const connectShoutbox = () => (dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
const token = getMeToken(state);
|
||||
const accountUrl = getMeUrl(state);
|
||||
@ -45,8 +54,14 @@ type ShoutboxAction =
|
||||
type: typeof SHOUTBOX_CONNECT;
|
||||
socket: ReturnType<(InstanceType<typeof PlApiClient>)['shoutbox']['connect']>;
|
||||
}
|
||||
| ReturnType<typeof importShoutboxMessages>
|
||||
| ReturnType<typeof importShoutboxMessage>;
|
||||
| {
|
||||
type: typeof SHOUTBOX_MESSAGE_IMPORT;
|
||||
message: ShoutMessage;
|
||||
}
|
||||
| {
|
||||
type: typeof SHOUTBOX_MESSAGES_IMPORT;
|
||||
messages: ShoutMessage[];
|
||||
}
|
||||
|
||||
export {
|
||||
SHOUTBOX_MESSAGES_IMPORT,
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ParsedContent } from 'pl-fe/components/parsed-content';
|
||||
import Avatar from 'pl-fe/components/ui/avatar';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { usePlFeConfig } from 'pl-fe/hooks/use-pl-fe-config';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
|
||||
interface IChatListShoutboxInterface {
|
||||
onClick: (chat: Chat | 'shoutbox') => void;
|
||||
}
|
||||
|
||||
const ChatListShoutbox: React.FC<IChatListShoutboxInterface> = ({ onClick }) => {
|
||||
const { logo } = usePlFeConfig();
|
||||
const messages = useAppSelector((state) => state.shoutbox.messages);
|
||||
|
||||
const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
onClick('shoutbox');
|
||||
}
|
||||
};
|
||||
|
||||
const lastMessage = messages.at(-1);
|
||||
|
||||
return (
|
||||
<div
|
||||
role='button'
|
||||
key='shoutbox'
|
||||
onClick={() => onClick('shoutbox')}
|
||||
onKeyDown={handleKeyDown}
|
||||
className='group flex w-full flex-col rounded-lg px-2 py-3 hover:bg-gray-100 focus:shadow-inset-ring dark:hover:bg-gray-800'
|
||||
data-testid='chat-list-item'
|
||||
tabIndex={0}
|
||||
>
|
||||
<HStack alignItems='center' justifyContent='between' space={2} className='w-full'>
|
||||
<HStack alignItems='center' space={2} className='overflow-hidden'>
|
||||
<Avatar src={logo} alt='' size={40} className='flex-none' />
|
||||
<Stack alignItems='start' className='overflow-hidden'>
|
||||
<div className='flex w-full grow items-center space-x-1'>
|
||||
<Text weight='bold' size='sm' align='left' truncate>
|
||||
<FormattedMessage id='chat_list_item_shoutbox' defaultMessage='Instance shoutbox' />
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{lastMessage && (
|
||||
<>
|
||||
<Text
|
||||
align='left'
|
||||
size='sm'
|
||||
weight='medium'
|
||||
theme='default'
|
||||
truncate
|
||||
className='truncate-child pointer-events-none h-5 w-full'
|
||||
>
|
||||
<Text weight='bold' size='sm' align='left' truncate tag='span'>{lastMessage.author.display_name || `@${lastMessage.author.username}`}: </Text>
|
||||
<ParsedContent html={lastMessage.text} />
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ChatListShoutbox as default };
|
||||
@ -6,18 +6,25 @@ import PullToRefresh from 'pl-fe/components/pull-to-refresh';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import PlaceholderChat from 'pl-fe/features/placeholder/components/placeholder-chat';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useChats } from 'pl-fe/queries/chats';
|
||||
|
||||
import ChatListItem from './chat-list-item';
|
||||
import ChatListShoutbox from './chat-list-shoutbox';
|
||||
|
||||
import type { Chat } from 'pl-api';
|
||||
|
||||
interface IChatList {
|
||||
onClickChat: (chat: any) => void;
|
||||
onClickChat: (chat: Chat | 'shoutbox') => void;
|
||||
useWindowScroll?: boolean;
|
||||
}
|
||||
|
||||
const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false }) => {
|
||||
const { shoutbox } = useFeatures();
|
||||
const { chatsQuery: { data: chats, isFetching, hasNextPage, fetchNextPage, refetch } } = useChats();
|
||||
|
||||
const allChats: Array<Chat | 'shoutbox'> | undefined = shoutbox ? ['shoutbox', ...(chats || [])] : chats;
|
||||
|
||||
const [isNearBottom, setNearBottom] = useState<boolean>(false);
|
||||
const [isNearTop, setNearTop] = useState<boolean>(true);
|
||||
|
||||
@ -50,11 +57,11 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false })
|
||||
atTopStateChange={(atTop) => setNearTop(atTop)}
|
||||
atBottomStateChange={(atBottom) => setNearBottom(atBottom)}
|
||||
useWindowScroll={useWindowScroll}
|
||||
data={chats}
|
||||
data={allChats}
|
||||
endReached={handleLoadMore}
|
||||
itemContent={(_index, chat) => (
|
||||
<div className='px-2'>
|
||||
<ChatListItem chat={chat} onClick={onClickChat} />
|
||||
{chat === 'shoutbox' ? <ChatListShoutbox onClick={onClickChat} /> : <ChatListItem chat={chat} onClick={onClickChat} />}
|
||||
</div>
|
||||
)}
|
||||
components={{
|
||||
|
||||
@ -19,8 +19,8 @@ const ChatPageSidebar = () => {
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const handleClickChat = (chat: Chat) => {
|
||||
history.push(`/chats/${chat.id}`);
|
||||
const handleClickChat = (chat: Chat | 'shoutbox') => {
|
||||
history.push(`/chats/${chat === 'shoutbox' ? 'shoutbox' : chat.id}`);
|
||||
};
|
||||
|
||||
const handleChatCreate = () => {
|
||||
|
||||
@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useChats } from 'pl-fe/queries/chats';
|
||||
|
||||
import ChatList from '../chat-list';
|
||||
@ -19,16 +20,21 @@ import type { Chat } from 'pl-api';
|
||||
|
||||
const ChatPane = () => {
|
||||
const { unreadChatsCount } = useStatContext();
|
||||
const { shoutbox } = useFeatures();
|
||||
|
||||
const { screen, changeScreen, isOpen, toggleChatPane } = useChatContext();
|
||||
const { chatsQuery: { data: chats, isLoading } } = useChats();
|
||||
|
||||
const handleClickChat = (nextChat: Chat) => {
|
||||
changeScreen(ChatWidgetScreens.CHAT, nextChat.id);
|
||||
const handleClickChat = (nextChat: Chat | 'shoutbox') => {
|
||||
if (nextChat === 'shoutbox') {
|
||||
// changeScreen(ChatWidgetScreens.SHOUTBOX);
|
||||
} else {
|
||||
changeScreen(ChatWidgetScreens.CHAT, nextChat.id);
|
||||
}
|
||||
};
|
||||
|
||||
const renderBody = () => {
|
||||
if (Number(chats?.length) > 0 || isLoading) {
|
||||
if (Number(chats?.length) > 0 || shoutbox || isLoading) {
|
||||
return (
|
||||
<Stack space={4} className='h-full grow'>
|
||||
<ChatList onClickChat={handleClickChat} />
|
||||
|
||||
@ -8,6 +8,7 @@ import { fetchFilters } from 'pl-fe/actions/filters';
|
||||
import { fetchMarker } from 'pl-fe/actions/markers';
|
||||
import { expandNotifications } from 'pl-fe/actions/notifications';
|
||||
import { register as registerPushNotifications } from 'pl-fe/actions/push-notifications/registerer';
|
||||
import { connectShoutbox } from 'pl-fe/actions/shoutbox';
|
||||
import { fetchHomeTimeline } from 'pl-fe/actions/timelines';
|
||||
import { useUserStream } from 'pl-fe/api/hooks/streaming/use-user-stream';
|
||||
import SidebarNavigation from 'pl-fe/components/sidebar-navigation';
|
||||
@ -421,6 +422,10 @@ const UI: React.FC<IUI> = React.memo(({ children }) => {
|
||||
setTimeout(() => prefetchFollowRequests(client), 700);
|
||||
}
|
||||
|
||||
if (features.shoutbox) {
|
||||
dispatch(connectShoutbox());
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
queryClient.prefetchInfiniteQuery(scheduledStatusesQueryOptions);
|
||||
}, 900);
|
||||
|
||||
Reference in New Issue
Block a user