Add error states
This commit is contained in:
parent
c48b4adc81
commit
1c179cd4a0
|
@ -27,7 +27,7 @@ const ChatMessageListIntro = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReportChat = () => {
|
const handleReportChat = () => {
|
||||||
dispatch(initReport(chat?.account));
|
dispatch(initReport(chat?.account as any));
|
||||||
acceptChat.mutate();
|
acceptChat.mutate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@ import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { initReportById } from 'soapbox/actions/reports';
|
import { Avatar, Button, Divider, HStack, Spinner, Stack, Text } from 'soapbox/components/ui';
|
||||||
import { Avatar, Divider, HStack, Spinner, Stack, Text } from 'soapbox/components/ui';
|
|
||||||
import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container';
|
import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container';
|
||||||
// import emojify from 'soapbox/features/emoji/emoji';
|
// import emojify from 'soapbox/features/emoji/emoji';
|
||||||
import PlaceholderChatMessage from 'soapbox/features/placeholder/components/placeholder-chat-message';
|
import PlaceholderChatMessage from 'soapbox/features/placeholder/components/placeholder-chat-message';
|
||||||
|
@ -68,10 +67,21 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
const [scrollPosition, setScrollPosition] = useState(0);
|
const [scrollPosition, setScrollPosition] = useState(0);
|
||||||
|
|
||||||
const { deleteChatMessage, markChatAsRead } = useChat(chat.id);
|
const { deleteChatMessage, markChatAsRead } = useChat(chat.id);
|
||||||
const { data: chatMessages, isLoading, isFetching, isFetched, fetchNextPage, isFetchingNextPage, isPlaceholderData } = useChatMessages(chat.id);
|
const {
|
||||||
|
data: chatMessages,
|
||||||
|
fetchNextPage,
|
||||||
|
isError,
|
||||||
|
isFetched,
|
||||||
|
isFetching,
|
||||||
|
isFetchingNextPage,
|
||||||
|
isLoading,
|
||||||
|
isPlaceholderData,
|
||||||
|
refetch,
|
||||||
|
} = useChatMessages(chat.id);
|
||||||
const formattedChatMessages = chatMessages || [];
|
const formattedChatMessages = chatMessages || [];
|
||||||
|
|
||||||
const me = useAppSelector(state => state.me);
|
const me = useAppSelector((state) => state.me);
|
||||||
|
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account, 'blocked_by']));
|
||||||
|
|
||||||
const node = useRef<HTMLDivElement>(null);
|
const node = useRef<HTMLDivElement>(null);
|
||||||
const messagesEnd = useRef<HTMLDivElement>(null);
|
const messagesEnd = useRef<HTMLDivElement>(null);
|
||||||
|
@ -89,16 +99,14 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFormattedTimestamp = (chatMessage: ChatMessageEntity) => {
|
const getFormattedTimestamp = (chatMessage: ChatMessageEntity) => {
|
||||||
return intl.formatDate(
|
return intl.formatDate(new Date(chatMessage.created_at), {
|
||||||
new Date(chatMessage.created_at), {
|
|
||||||
hour12: false,
|
hour12: false,
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
},
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setBubbleRef = (c: HTMLDivElement) => {
|
const setBubbleRef = (c: HTMLDivElement) => {
|
||||||
|
@ -295,7 +303,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={classNames({ 'order-1': !isMyMessage })}>
|
<div className={classNames({ 'order-1': !isMyMessage })}>
|
||||||
<Avatar src={isMyMessage ? account?.avatar : chat.account.avatar} size={34} />
|
<Avatar src={isMyMessage ? account?.avatar as string : chat.account.avatar as string} size={34} />
|
||||||
</div>
|
</div>
|
||||||
</HStack>
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
@ -383,6 +391,44 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isBlocked) {
|
||||||
|
return (
|
||||||
|
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
|
||||||
|
<Stack alignItems='center' space={2}>
|
||||||
|
<Avatar src={chat.account.avatar_static} size={75} />
|
||||||
|
<Text align='center'>
|
||||||
|
<>
|
||||||
|
<Text tag='span'>You are blocked by</Text>
|
||||||
|
{' '}
|
||||||
|
<Text tag='span' theme='primary'>@{chat.account.acct}</Text>
|
||||||
|
</>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
|
||||||
|
<Stack space={4}>
|
||||||
|
<Stack space={1}>
|
||||||
|
<Text size='lg' weight='bold' align='center'>Whoops!</Text>
|
||||||
|
<Text theme='muted' align='center'>
|
||||||
|
We encountered a network failure.
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<div className='mx-auto'>
|
||||||
|
<Button theme='primary' onClick={() => refetch()}>
|
||||||
|
Try again
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full flex flex-col px-4 flex-grow overflow-y-scroll space-y-6' onScroll={handleScroll} ref={node}> {/* style={{ height: autosize ? 'calc(100vh - 16rem)' : undefined }} */}
|
<div className='h-full flex flex-col px-4 flex-grow overflow-y-scroll space-y-6' onScroll={handleScroll} ref={node}> {/* style={{ height: autosize ? 'calc(100vh - 16rem)' : undefined }} */}
|
||||||
{!isLoading ? (
|
{!isLoading ? (
|
||||||
|
|
|
@ -31,7 +31,7 @@ const ChatSettings = () => {
|
||||||
message: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.',
|
message: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.',
|
||||||
confirm: 'Block',
|
confirm: 'Block',
|
||||||
confirmationTheme: 'primary',
|
confirmationTheme: 'primary',
|
||||||
onConfirm: () => dispatch(blockAccount(chat?.account.id)),
|
onConfirm: () => dispatch(blockAccount(chat?.account.id as string)),
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ const ChatSettings = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReportChat = () => {
|
const handleReportChat = () => {
|
||||||
dispatch(initReport(chat?.account));
|
dispatch(initReport(chat?.account as any));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
export type IAccount = {
|
||||||
|
acct: string
|
||||||
|
avatar: string
|
||||||
|
avatar_static: string
|
||||||
|
bot: boolean
|
||||||
|
created_at: string
|
||||||
|
discoverable: boolean
|
||||||
|
display_name: string
|
||||||
|
followers_count: number
|
||||||
|
following_count: number
|
||||||
|
group: boolean
|
||||||
|
header: string
|
||||||
|
header_static: string
|
||||||
|
id: string
|
||||||
|
last_status_at: string
|
||||||
|
location: string
|
||||||
|
locked: boolean
|
||||||
|
note: string
|
||||||
|
statuses_count: number
|
||||||
|
url: string
|
||||||
|
username: string
|
||||||
|
verified: boolean
|
||||||
|
website: string
|
||||||
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
|
import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { fetchRelationships } from 'soapbox/actions/accounts';
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
import { useChatContext } from 'soapbox/contexts/chat-context';
|
||||||
import { useApi, useAppDispatch } from 'soapbox/hooks';
|
import { useApi, useAppDispatch } from 'soapbox/hooks';
|
||||||
|
|
||||||
import { queryClient } from './client';
|
import { queryClient } from './client';
|
||||||
|
|
||||||
|
import type { IAccount } from './accounts';
|
||||||
|
|
||||||
export interface IChat {
|
export interface IChat {
|
||||||
id: string
|
id: string
|
||||||
unread: number
|
unread: number
|
||||||
|
@ -24,7 +27,7 @@ export interface IChat {
|
||||||
updated_at: Date
|
updated_at: Date
|
||||||
accepted: boolean
|
accepted: boolean
|
||||||
discarded_at: null | string
|
discarded_at: null | string
|
||||||
account: any
|
account: IAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChatMessage {
|
export interface IChatMessage {
|
||||||
|
@ -88,9 +91,10 @@ const useChatMessages = (chatId: string) => {
|
||||||
|
|
||||||
const useChats = () => {
|
const useChats = () => {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const getChats = async(pageParam?: any): Promise<{ result: IChat[], maxId: string, hasMore: boolean }> => {
|
const getChats = async(pageParam?: any): Promise<{ result: IChat[], maxId: string, hasMore: boolean }> => {
|
||||||
const { data, headers } = await api.get('/api/v1/pleroma/chats', {
|
const { data, headers } = await api.get<IChat[]>('/api/v1/pleroma/chats', {
|
||||||
params: {
|
params: {
|
||||||
max_id: pageParam?.maxId,
|
max_id: pageParam?.maxId,
|
||||||
},
|
},
|
||||||
|
@ -99,6 +103,9 @@ const useChats = () => {
|
||||||
const hasMore = !!headers.link;
|
const hasMore = !!headers.link;
|
||||||
const nextMaxId = data[data.length - 1]?.id;
|
const nextMaxId = data[data.length - 1]?.id;
|
||||||
|
|
||||||
|
// Set the relationships to these users in the redux store.
|
||||||
|
dispatch(fetchRelationships(data.map((item) => item.account.id)));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: data,
|
result: data,
|
||||||
maxId: nextMaxId,
|
maxId: nextMaxId,
|
||||||
|
|
|
@ -5,34 +5,11 @@ import { importFetchedAccounts } from 'soapbox/actions/importer';
|
||||||
import { getLinks } from 'soapbox/api';
|
import { getLinks } from 'soapbox/api';
|
||||||
import { useApi, useAppDispatch } from 'soapbox/hooks';
|
import { useApi, useAppDispatch } from 'soapbox/hooks';
|
||||||
|
|
||||||
type Account = {
|
import type { IAccount } from './accounts';
|
||||||
acct: string
|
|
||||||
avatar: string
|
|
||||||
avatar_static: string
|
|
||||||
bot: boolean
|
|
||||||
created_at: string
|
|
||||||
discoverable: boolean
|
|
||||||
display_name: string
|
|
||||||
followers_count: number
|
|
||||||
following_count: number
|
|
||||||
group: boolean
|
|
||||||
header: string
|
|
||||||
header_static: string
|
|
||||||
id: string
|
|
||||||
last_status_at: string
|
|
||||||
location: string
|
|
||||||
locked: boolean
|
|
||||||
note: string
|
|
||||||
statuses_count: number
|
|
||||||
url: string
|
|
||||||
username: string
|
|
||||||
verified: boolean
|
|
||||||
website: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Suggestion = {
|
type Suggestion = {
|
||||||
source: 'staff'
|
source: 'staff'
|
||||||
account: Account
|
account: IAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue