fixed images flickering and infinite loop on fetching previous messages
This commit is contained in:
48
client/src/components/chat/AttachmentPreview.tsx
Normal file
48
client/src/components/chat/AttachmentPreview.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import LoadingWheel from './LoadingWheel';
|
||||
import FileBox from './FileBox';
|
||||
|
||||
// Cache to keep track of loaded images
|
||||
const loadedImages = new Set<string>();
|
||||
|
||||
const AttachmentPreview = ({ url }: { url: string }) => {
|
||||
const isImage = url.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i);
|
||||
const [isLoading, setIsLoading] = useState(!loadedImages.has(url));
|
||||
|
||||
useEffect(() => {
|
||||
if (isImage && !loadedImages.has(url)) {
|
||||
// Preload image
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
loadedImages.add(url);
|
||||
setIsLoading(false);
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
}, [url, isImage]);
|
||||
|
||||
if (!isImage) {
|
||||
return <FileBox url={url} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative min-h-64 w-full">
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 rounded">
|
||||
<LoadingWheel />
|
||||
</div>
|
||||
)}
|
||||
<a href={url} target="_blank">
|
||||
<img
|
||||
src={url}
|
||||
alt="attachment"
|
||||
className={`max-w-full max-h-64 object-contain rounded transition-opacity duration-200 ${
|
||||
isLoading ? 'opacity-0' : 'opacity-100'
|
||||
}`}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AttachmentPreview;
|
||||
@@ -8,6 +8,7 @@ import FileBox from './FileBox.tsx';
|
||||
import { ContactsProps } from '../../pages/Chat.tsx';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { axiosClient } from '../../App.tsx';
|
||||
import AttachmentPreview from './AttachmentPreview.tsx';
|
||||
|
||||
type MessagesAreaProps = {
|
||||
messages: ChatMessages[];
|
||||
@@ -19,6 +20,7 @@ type MessagesAreaProps = {
|
||||
setContactsList: React.Dispatch<React.SetStateAction<ContactsProps[]>>;
|
||||
fetchPreviousMessages: (contact: number | null) => Promise<void>;
|
||||
errorMessage: string | null;
|
||||
hasMoreMessages: boolean;
|
||||
};
|
||||
|
||||
function MessagesArea({
|
||||
@@ -30,6 +32,7 @@ function MessagesArea({
|
||||
fetchPreviousMessages,
|
||||
errorMessage,
|
||||
setMessages,
|
||||
hasMoreMessages,
|
||||
}: MessagesAreaProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { username }: { username: string } = useOutletContext();
|
||||
@@ -164,33 +167,6 @@ function MessagesArea({
|
||||
scrollToBottom();
|
||||
}, []);
|
||||
|
||||
const AttachmentPreview = ({ url }: { url: string }) => {
|
||||
const isImage = url.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
if (!isImage) {
|
||||
return <FileBox url={url} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative min-h-64 w-full">
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 rounded">
|
||||
<LoadingWheel />
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
src={url}
|
||||
alt="attachment"
|
||||
className={`max-w-full max-h-64 object-contain rounded transition-opacity duration-200 ${
|
||||
isLoading ? 'opacity-0' : 'opacity-100'
|
||||
}`}
|
||||
onLoad={() => setIsLoading(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const messageList = messages.map((msg: ChatMessages) => (
|
||||
<li
|
||||
className={`whitespace-pre-wrap ml-2 rounded p-1 group ${
|
||||
|
||||
@@ -38,7 +38,7 @@ function Chat() {
|
||||
const [cursor, setCursor] = useState<number>(0);
|
||||
const [messages, setMessages] = useState<ChatMessages[]>([]);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [hasMoreMessages, setHasMoreMessages] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
const token = Cookies.get('token');
|
||||
@@ -51,7 +51,6 @@ function Chat() {
|
||||
const parsedContact = JSON.parse(storedContact);
|
||||
if (parsedContact) {
|
||||
initializeContact(parsedContact);
|
||||
setIsLoading(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse stored contact:', error);
|
||||
@@ -80,7 +79,6 @@ function Chat() {
|
||||
|
||||
const fetchMessages = async (conversation_id: number) => {
|
||||
console.log('Fetching messages for: ', conversation_id);
|
||||
|
||||
await getMessages(conversation_id)
|
||||
.then((messages) => {
|
||||
setCursor(() => {
|
||||
@@ -90,32 +88,48 @@ function Chat() {
|
||||
});
|
||||
|
||||
messages.messages.forEach(messageHandler);
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch((error) => setErrorMessage(error.response.data.message));
|
||||
};
|
||||
|
||||
const fetchPreviousMessages = async (contact: number | null) => {
|
||||
console.log('Fetching messages for: ', contact, 'with cursor: ', cursor);
|
||||
console.log(
|
||||
'Fetching messages for: ',
|
||||
contact,
|
||||
'with cursor: ',
|
||||
cursor,
|
||||
'hasmoremessages: ',
|
||||
hasMoreMessages,
|
||||
);
|
||||
|
||||
await getMessages(contact, cursor, 50)
|
||||
.then((messages) => {
|
||||
messages.messages.forEach((msg) => {
|
||||
setMessages((prevMessages) => {
|
||||
if (!prevMessages.some((m) => m.message_id === msg.message_id)) {
|
||||
return [msg, ...prevMessages];
|
||||
}
|
||||
return prevMessages;
|
||||
});
|
||||
});
|
||||
if (!hasMoreMessages) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCursor(() => {
|
||||
return Math.min(
|
||||
...messages.messages.map((message) => message.message_id),
|
||||
);
|
||||
try {
|
||||
const messages = await getMessages(contact, cursor, 50);
|
||||
console.log('fetchpreviousmessages: ', messages.messages.length);
|
||||
if (messages.messages.length < 50) {
|
||||
setHasMoreMessages(false);
|
||||
setErrorMessage('No more messages found');
|
||||
}
|
||||
messages.messages.forEach((msg) => {
|
||||
setMessages((prevMessages) => {
|
||||
if (!prevMessages.some((m) => m.message_id === msg.message_id)) {
|
||||
return [msg, ...prevMessages];
|
||||
}
|
||||
return prevMessages;
|
||||
});
|
||||
})
|
||||
.catch((error) => setErrorMessage(error.response.data.message));
|
||||
});
|
||||
|
||||
setCursor(() => {
|
||||
return Math.min(
|
||||
...messages.messages.map((message) => message.message_id),
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
setErrorMessage(e.response.data.message);
|
||||
}
|
||||
};
|
||||
function messageHandler(msg: ChatMessages) {
|
||||
setMessages((prevMessages) => {
|
||||
@@ -180,6 +194,7 @@ function Chat() {
|
||||
setContactsList={setContactsList}
|
||||
fetchPreviousMessages={fetchPreviousMessages}
|
||||
errorMessage={errorMessage}
|
||||
hasMoreMessages={hasMoreMessages}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-shrink-0 mb-2 mt-0">
|
||||
|
||||
Reference in New Issue
Block a user