diff --git a/packages/pl-fe/src/actions/shoutbox.ts b/packages/pl-fe/src/actions/shoutbox.ts index 36a527ed5..da78484af 100644 --- a/packages/pl-fe/src/actions/shoutbox.ts +++ b/packages/pl-fe/src/actions/shoutbox.ts @@ -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)['shoutbox']['connect']>; } - | ReturnType - | ReturnType; + | { + type: typeof SHOUTBOX_MESSAGE_IMPORT; + message: ShoutMessage; + } + | { + type: typeof SHOUTBOX_MESSAGES_IMPORT; + messages: ShoutMessage[]; + } export { SHOUTBOX_MESSAGES_IMPORT, diff --git a/packages/pl-fe/src/features/chats/components/chat-list-shoutbox.tsx b/packages/pl-fe/src/features/chats/components/chat-list-shoutbox.tsx new file mode 100644 index 000000000..d0359a889 --- /dev/null +++ b/packages/pl-fe/src/features/chats/components/chat-list-shoutbox.tsx @@ -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 = ({ onClick }) => { + const { logo } = usePlFeConfig(); + const messages = useAppSelector((state) => state.shoutbox.messages); + + const handleKeyDown: React.KeyboardEventHandler = (event) => { + if (event.key === 'Enter' || event.key === ' ') { + onClick('shoutbox'); + } + }; + + const lastMessage = messages.at(-1); + + return ( +
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} + > + + + + +
+ + + +
+ + {lastMessage && ( + <> + + {lastMessage.author.display_name || `@${lastMessage.author.username}`}: + + + + )} +
+
+
+
+ ); +}; + +export { ChatListShoutbox as default }; diff --git a/packages/pl-fe/src/features/chats/components/chat-list.tsx b/packages/pl-fe/src/features/chats/components/chat-list.tsx index 0bef1d925..a46256e68 100644 --- a/packages/pl-fe/src/features/chats/components/chat-list.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-list.tsx @@ -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 = ({ onClickChat, useWindowScroll = false }) => { + const { shoutbox } = useFeatures(); const { chatsQuery: { data: chats, isFetching, hasNextPage, fetchNextPage, refetch } } = useChats(); + const allChats: Array | undefined = shoutbox ? ['shoutbox', ...(chats || [])] : chats; + const [isNearBottom, setNearBottom] = useState(false); const [isNearTop, setNearTop] = useState(true); @@ -50,11 +57,11 @@ const ChatList: React.FC = ({ onClickChat, useWindowScroll = false }) atTopStateChange={(atTop) => setNearTop(atTop)} atBottomStateChange={(atBottom) => setNearBottom(atBottom)} useWindowScroll={useWindowScroll} - data={chats} + data={allChats} endReached={handleLoadMore} itemContent={(_index, chat) => (
- + {chat === 'shoutbox' ? : }
)} components={{ diff --git a/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-sidebar.tsx b/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-sidebar.tsx index 11770b503..3d05ebb32 100644 --- a/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-sidebar.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-sidebar.tsx @@ -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 = () => { diff --git a/packages/pl-fe/src/features/chats/components/chat-pane/chat-pane.tsx b/packages/pl-fe/src/features/chats/components/chat-pane/chat-pane.tsx index e3d2ee62f..fedb29743 100644 --- a/packages/pl-fe/src/features/chats/components/chat-pane/chat-pane.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-pane/chat-pane.tsx @@ -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 ( diff --git a/packages/pl-fe/src/features/ui/index.tsx b/packages/pl-fe/src/features/ui/index.tsx index 6774c19cc..aed4d58e9 100644 --- a/packages/pl-fe/src/features/ui/index.tsx +++ b/packages/pl-fe/src/features/ui/index.tsx @@ -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 = React.memo(({ children }) => { setTimeout(() => prefetchFollowRequests(client), 700); } + if (features.shoutbox) { + dispatch(connectShoutbox()); + } + setTimeout(() => { queryClient.prefetchInfiniteQuery(scheduledStatusesQueryOptions); }, 900);