Update ChatDetail.tsx
This commit is contained in:
@@ -12,6 +12,7 @@ import axios from "axios";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { ArrowLeft, Send, RefreshCw, File, FileText, Image as ImageIcon, Download } from "lucide-react";
|
import { ArrowLeft, Send, RefreshCw, File, FileText, Image as ImageIcon, Download } from "lucide-react";
|
||||||
import { getCookie } from "@/lib/client-utils";
|
import { getCookie } from "@/lib/client-utils";
|
||||||
|
import { ImageViewerModal } from "@/components/modals/image-viewer-modal";
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
_id: string;
|
_id: string;
|
||||||
@@ -88,6 +89,9 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
|||||||
const [previousMessageCount, setPreviousMessageCount] = useState<number>(0);
|
const [previousMessageCount, setPreviousMessageCount] = useState<number>(0);
|
||||||
const audioRef = useRef<HTMLAudioElement | null>(null);
|
const audioRef = useRef<HTMLAudioElement | null>(null);
|
||||||
const markReadTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const markReadTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const [selectedImage, setSelectedImage] = useState<string | null>(null);
|
||||||
|
const [selectedMessageIndex, setSelectedMessageIndex] = useState<number | null>(null);
|
||||||
|
const [selectedAttachmentIndex, setSelectedAttachmentIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
// Initialize audio element
|
// Initialize audio element
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -290,6 +294,58 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
|||||||
router.push("/dashboard/chats");
|
router.push("/dashboard/chats");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add function to handle image navigation
|
||||||
|
const handleImageNavigation = (direction: 'prev' | 'next') => {
|
||||||
|
if (!chat || selectedMessageIndex === null || selectedAttachmentIndex === null) return;
|
||||||
|
|
||||||
|
const currentMessage = chat.messages[selectedMessageIndex];
|
||||||
|
const currentAttachments = currentMessage.attachments.filter(att =>
|
||||||
|
/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff)($|\?)/i.test(att) ||
|
||||||
|
att.includes('/photos/') ||
|
||||||
|
att.includes('/photo/')
|
||||||
|
);
|
||||||
|
|
||||||
|
let newAttachmentIndex = selectedAttachmentIndex;
|
||||||
|
let newMessageIndex = selectedMessageIndex;
|
||||||
|
|
||||||
|
if (direction === 'next') {
|
||||||
|
newAttachmentIndex++;
|
||||||
|
if (newAttachmentIndex >= currentAttachments.length) {
|
||||||
|
newAttachmentIndex = 0;
|
||||||
|
newMessageIndex++;
|
||||||
|
if (newMessageIndex >= chat.messages.length) {
|
||||||
|
newMessageIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newAttachmentIndex--;
|
||||||
|
if (newAttachmentIndex < 0) {
|
||||||
|
newMessageIndex--;
|
||||||
|
if (newMessageIndex < 0) {
|
||||||
|
newMessageIndex = chat.messages.length - 1;
|
||||||
|
}
|
||||||
|
const prevMessage = chat.messages[newMessageIndex];
|
||||||
|
const prevAttachments = prevMessage.attachments.filter(att =>
|
||||||
|
/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff)($|\?)/i.test(att) ||
|
||||||
|
att.includes('/photos/') ||
|
||||||
|
att.includes('/photo/')
|
||||||
|
);
|
||||||
|
newAttachmentIndex = prevAttachments.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedMessageIndex(newMessageIndex);
|
||||||
|
setSelectedAttachmentIndex(newAttachmentIndex);
|
||||||
|
setSelectedImage(currentAttachments[newAttachmentIndex]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the image click handler
|
||||||
|
const handleImageClick = (imageUrl: string, messageIndex: number, attachmentIndex: number) => {
|
||||||
|
setSelectedImage(imageUrl);
|
||||||
|
setSelectedMessageIndex(messageIndex);
|
||||||
|
setSelectedAttachmentIndex(attachmentIndex);
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-screen w-full relative">
|
<div className="flex flex-col h-screen w-full relative">
|
||||||
@@ -340,9 +396,9 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
|||||||
<p className="text-muted-foreground">No messages yet. Send one to start the conversation.</p>
|
<p className="text-muted-foreground">No messages yet. Send one to start the conversation.</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
chat.messages.map((msg, index) => (
|
chat.messages.map((msg, messageIndex) => (
|
||||||
<div
|
<div
|
||||||
key={msg._id || index}
|
key={msg._id || messageIndex}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex",
|
"flex",
|
||||||
msg.sender === "vendor" ? "justify-end" : "justify-start"
|
msg.sender === "vendor" ? "justify-end" : "justify-start"
|
||||||
@@ -372,17 +428,19 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
|||||||
{/* Show attachments if any */}
|
{/* Show attachments if any */}
|
||||||
{msg.attachments && msg.attachments.length > 0 && (
|
{msg.attachments && msg.attachments.length > 0 && (
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
{msg.attachments.map((attachment, idx) => {
|
{msg.attachments.map((attachment, attachmentIndex) => {
|
||||||
// Check if attachment is an image by looking at the URL extension or paths
|
const isImage = /\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff)($|\?)/i.test(attachment) ||
|
||||||
const isImage = /\.(jpg|jpeg|png|gif|webp)($|\?)/i.test(attachment) ||
|
attachment.includes('/photos/') ||
|
||||||
attachment.includes('/photos/') ||
|
attachment.includes('/photo/');
|
||||||
attachment.includes('/photo/');
|
|
||||||
|
|
||||||
const fileName = getFileNameFromUrl(attachment);
|
const fileName = getFileNameFromUrl(attachment);
|
||||||
|
|
||||||
return isImage ? (
|
return isImage ? (
|
||||||
// Render image attachment
|
<div
|
||||||
<div key={`attachment-${idx}`} className="rounded-md overflow-hidden bg-background/20 p-1">
|
key={`attachment-${attachmentIndex}`}
|
||||||
|
className="rounded-md overflow-hidden bg-background/20 p-1"
|
||||||
|
onClick={() => handleImageClick(attachment, messageIndex, attachmentIndex)}
|
||||||
|
>
|
||||||
<div className="flex justify-between items-center mb-1">
|
<div className="flex justify-between items-center mb-1">
|
||||||
<span className="text-xs opacity-70 flex items-center">
|
<span className="text-xs opacity-70 flex items-center">
|
||||||
<ImageIcon className="h-3 w-3 mr-1" />
|
<ImageIcon className="h-3 w-3 mr-1" />
|
||||||
@@ -399,21 +457,18 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
|||||||
<Download className="h-3 w-3" />
|
<Download className="h-3 w-3" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href={attachment} target="_blank" rel="noopener noreferrer">
|
<img
|
||||||
<img
|
src={attachment}
|
||||||
src={attachment}
|
alt={fileName}
|
||||||
alt={fileName}
|
className="max-w-full max-h-60 object-contain cursor-pointer hover:opacity-90 transition-opacity rounded"
|
||||||
className="max-w-full max-h-60 object-contain cursor-pointer hover:opacity-90 transition-opacity rounded"
|
onError={(e) => {
|
||||||
onError={(e) => {
|
(e.target as HTMLImageElement).src = "/placeholder-image.svg";
|
||||||
// Fallback for broken images
|
}}
|
||||||
(e.target as HTMLImageElement).src = "/placeholder-image.svg";
|
/>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
// Render file attachment
|
// Render file attachment
|
||||||
<div key={`attachment-${idx}`} className="flex items-center bg-background/20 rounded-md p-2 hover:bg-background/30 transition-colors">
|
<div key={`attachment-${attachmentIndex}`} className="flex items-center bg-background/20 rounded-md p-2 hover:bg-background/30 transition-colors">
|
||||||
<div className="mr-2">
|
<div className="mr-2">
|
||||||
{getFileIcon(attachment)}
|
{getFileIcon(attachment)}
|
||||||
</div>
|
</div>
|
||||||
@@ -457,6 +512,18 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Update the image viewer modal */}
|
||||||
|
<ImageViewerModal
|
||||||
|
isOpen={!!selectedImage}
|
||||||
|
onClose={() => {
|
||||||
|
setSelectedImage(null);
|
||||||
|
setSelectedMessageIndex(null);
|
||||||
|
setSelectedAttachmentIndex(null);
|
||||||
|
}}
|
||||||
|
imageUrl={selectedImage || ""}
|
||||||
|
onNavigate={handleImageNavigation}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user