Add snooze icon to ChatList
This commit is contained in:
parent
8e35d1dd92
commit
4352d17217
|
@ -1,17 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import RelativeTimestamp from 'soapbox/components/relative-timestamp';
|
import RelativeTimestamp from 'soapbox/components/relative-timestamp';
|
||||||
import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
|
import { Avatar, HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
|
|
||||||
import type { IChat } from 'soapbox/queries/chats';
|
import type { IChat, IChatSilence } from 'soapbox/queries/chats';
|
||||||
|
|
||||||
interface IChatListItemInterface {
|
interface IChatListItemInterface {
|
||||||
chat: IChat,
|
chat: IChat,
|
||||||
onClick: (chat: any) => void,
|
onClick: (chat: any) => void,
|
||||||
|
chatSilence?: IChatSilence
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, onClick }) => {
|
const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, chatSilence, onClick }) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={chat.id}
|
key={chat.id}
|
||||||
|
@ -37,7 +38,7 @@ const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, onClick }) => {
|
||||||
weight='medium'
|
weight='medium'
|
||||||
theme='muted'
|
theme='muted'
|
||||||
truncate
|
truncate
|
||||||
className='w-full'
|
className='w-full truncate-child'
|
||||||
data-testid='chat-last-message'
|
data-testid='chat-last-message'
|
||||||
dangerouslySetInnerHTML={{ __html: chat.last_message?.content }}
|
dangerouslySetInnerHTML={{ __html: chat.last_message?.content }}
|
||||||
/>
|
/>
|
||||||
|
@ -45,23 +46,29 @@ const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, onClick }) => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
{chat.last_message && (
|
<HStack alignItems='center' space={2}>
|
||||||
<HStack alignItems='center' space={2}>
|
{chatSilence ? (
|
||||||
{chat.last_message.unread && (
|
<Icon src={require('@tabler/icons/bell-off.svg')} className='w-5 h-5 text-gray-600' />
|
||||||
<div
|
) : null}
|
||||||
className='w-2 h-2 rounded-full bg-secondary-500'
|
|
||||||
data-testid='chat-unread-indicator'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<RelativeTimestamp
|
{chat.last_message && (
|
||||||
timestamp={chat.last_message.created_at}
|
<>
|
||||||
align='right'
|
{chat.last_message.unread && (
|
||||||
size='xs'
|
<div
|
||||||
truncate
|
className='w-2 h-2 rounded-full bg-secondary-500'
|
||||||
/>
|
data-testid='chat-unread-indicator'
|
||||||
</HStack>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<RelativeTimestamp
|
||||||
|
timestamp={chat.last_message.created_at}
|
||||||
|
align='right'
|
||||||
|
size='xs'
|
||||||
|
truncate
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import PullToRefresh from 'soapbox/components/pull-to-refresh';
|
||||||
import { Stack } from 'soapbox/components/ui';
|
import { Stack } from 'soapbox/components/ui';
|
||||||
import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder-chat';
|
import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder-chat';
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { useChats } from 'soapbox/queries/chats';
|
import { useChats, useChatSilences } from 'soapbox/queries/chats';
|
||||||
|
|
||||||
import ChatListItem from './chat-list-item';
|
import ChatListItem from './chat-list-item';
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false, s
|
||||||
|
|
||||||
const { chatsQuery: { data: chats, isFetching, hasNextPage, fetchNextPage } } = useChats(searchValue);
|
const { chatsQuery: { data: chats, isFetching, hasNextPage, fetchNextPage } } = useChats(searchValue);
|
||||||
|
|
||||||
|
const { data: chatSilences } = useChatSilences();
|
||||||
|
|
||||||
const [isNearBottom, setNearBottom] = useState<boolean>(false);
|
const [isNearBottom, setNearBottom] = useState<boolean>(false);
|
||||||
const [isNearTop, setNearTop] = useState<boolean>(true);
|
const [isNearTop, setNearTop] = useState<boolean>(true);
|
||||||
|
|
||||||
|
@ -57,7 +59,10 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false, s
|
||||||
useWindowScroll={useWindowScroll}
|
useWindowScroll={useWindowScroll}
|
||||||
data={chats}
|
data={chats}
|
||||||
endReached={handleLoadMore}
|
endReached={handleLoadMore}
|
||||||
itemContent={(_index, chat) => <ChatListItem chat={chat} onClick={onClickChat} />}
|
itemContent={(_index, chat) => {
|
||||||
|
const chatSilence = chatSilences?.find((chatSilence) => String(chatSilence.target_account_id) === chat.account.id);
|
||||||
|
return <ChatListItem chat={chat} onClick={onClickChat} chatSilence={chatSilence} />;
|
||||||
|
}}
|
||||||
components={{
|
components={{
|
||||||
ScrollSeekPlaceholder: () => <PlaceholderChat />,
|
ScrollSeekPlaceholder: () => <PlaceholderChat />,
|
||||||
// Footer: () => hasNextPage ? <Spinner withText={false} /> : null,
|
// Footer: () => hasNextPage ? <Spinner withText={false} /> : null,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { Avatar, Card, CardTitle, Divider, HStack, Icon, IconButton, Menu, MenuB
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
import { useChatContext } from 'soapbox/contexts/chat-context';
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { useChat, useChatSilences } from 'soapbox/queries/chats';
|
import { useChat, useChatSilence } from 'soapbox/queries/chats';
|
||||||
|
|
||||||
import Chat from './chat';
|
import Chat from './chat';
|
||||||
import ChatList from './chat-list';
|
import ChatList from './chat-list';
|
||||||
|
@ -36,8 +36,8 @@ const ChatPage = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const { isSilenced, handleSilence } = useChatSilences();
|
|
||||||
const { chat, setChat } = useChatContext();
|
const { chat, setChat } = useChatContext();
|
||||||
|
const { isSilenced, handleSilence } = useChatSilence(chat);
|
||||||
const { deleteChat } = useChat(chat?.id as string);
|
const { deleteChat } = useChat(chat?.id as string);
|
||||||
|
|
||||||
const handleSuggestion = (accountId: string) => {
|
const handleSuggestion = (accountId: string) => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import List, { ListItem } from 'soapbox/components/list';
|
||||||
import { Avatar, Divider, HStack, Icon, Stack, Text, Toggle } from 'soapbox/components/ui';
|
import { Avatar, Divider, HStack, Icon, Stack, Text, Toggle } from 'soapbox/components/ui';
|
||||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
import { useChatContext } from 'soapbox/contexts/chat-context';
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { useChat, useChatSilences } from 'soapbox/queries/chats';
|
import { useChat, useChatSilence } from 'soapbox/queries/chats';
|
||||||
|
|
||||||
import ChatPaneHeader from './chat-pane-header';
|
import ChatPaneHeader from './chat-pane-header';
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ const ChatSettings = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const { isSilenced, handleSilence } = useChatSilences();
|
|
||||||
|
|
||||||
const { chat, setEditing, toggleChatPane } = useChatContext();
|
const { chat, setEditing, toggleChatPane } = useChatContext();
|
||||||
|
const { isSilenced, handleSilence } = useChatSilence(chat);
|
||||||
|
|
||||||
const { deleteChat } = useChat(chat?.id as string);
|
const { deleteChat } = useChat(chat?.id as string);
|
||||||
|
|
||||||
const closeSettings = () => setEditing(false);
|
const closeSettings = () => setEditing(false);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
|
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { fetchRelationships } from 'soapbox/actions/accounts';
|
import { fetchRelationships } from 'soapbox/actions/accounts';
|
||||||
|
@ -43,6 +43,12 @@ export interface IChatMessage {
|
||||||
pending?: boolean
|
pending?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IChatSilence {
|
||||||
|
id: number
|
||||||
|
account_id: number
|
||||||
|
target_account_id: number
|
||||||
|
}
|
||||||
|
|
||||||
const reverseOrder = (a: IChat, b: IChat): number => compareId(a.id, b.id);
|
const reverseOrder = (a: IChat, b: IChat): number => compareId(a.id, b.id);
|
||||||
|
|
||||||
const useChatMessages = (chatId: string) => {
|
const useChatMessages = (chatId: string) => {
|
||||||
|
@ -178,9 +184,22 @@ const useChat = (chatId: string) => {
|
||||||
|
|
||||||
const useChatSilences = () => {
|
const useChatSilences = () => {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
|
const getChatSilences = async() => {
|
||||||
|
const { data } = await api.get<IChatSilence[]>('/api/v1/pleroma/chats/silences');
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
return useQuery<IChatSilence[]>(['chatSilences'], getChatSilences, {
|
||||||
|
placeholderData: [],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const useChatSilence = (chat: IChat | null) => {
|
||||||
|
const api = useApi();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { chat } = useChatContext();
|
|
||||||
const [isSilenced, setSilenced] = useState<boolean>(false);
|
const [isSilenced, setSilenced] = useState<boolean>(false);
|
||||||
|
|
||||||
const getChatSilences = async() => {
|
const getChatSilences = async() => {
|
||||||
|
@ -235,9 +254,9 @@ const useChatSilences = () => {
|
||||||
if (chat?.id) {
|
if (chat?.id) {
|
||||||
fetchChatSilence();
|
fetchChatSilence();
|
||||||
}
|
}
|
||||||
}, [chat]);
|
}, [chat?.id]);
|
||||||
|
|
||||||
return { isSilenced, handleSilence };
|
return { isSilenced, handleSilence };
|
||||||
};
|
};
|
||||||
|
|
||||||
export { useChat, useChats, useChatMessages, useChatSilences };
|
export { useChat, useChats, useChatMessages, useChatSilences, useChatSilence };
|
||||||
|
|
|
@ -87,6 +87,10 @@
|
||||||
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 10%);
|
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.truncate-child > * {
|
||||||
|
@apply truncate;
|
||||||
|
}
|
||||||
|
|
||||||
.d-screen {
|
.d-screen {
|
||||||
height: 100vh; // Backwards compatibility
|
height: 100vh; // Backwards compatibility
|
||||||
/* stylelint-disable-next-line unit-no-unknown */
|
/* stylelint-disable-next-line unit-no-unknown */
|
||||||
|
|
Loading…
Reference in New Issue