Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions frontend/components/chat/MessageBubble.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Check, CheckCheck, Pin, Languages} from "lucide-react"
import Avatar from "./Avatar"
import { useState } from "react"
import { useState, useEffect, useRef } from "react"
import ReplyPreview from "./ReplyPreview"
import useChatStore from "../../src/store/useChatStore"

const formatTime = (d) =>
new Date(d).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
Expand All @@ -10,9 +11,18 @@ const formatTime = (d) =>
export default function MessageBubble({ msg, isMine, showTime, selectedUser, isOnline, authUser, onContextMenu, onTouchStart, onTouchEnd, onReact }) {

const [showTranslation, setShowTranslation] = useState(false)
const [selectedImage, setSelectedImage] = useState(null)
const [isSelected, setIsSelected] = useState(false)

const activeAudioId = useChatStore((state) => state.activeAudioId)
const setActiveAudioId = useChatStore((state) => state.setActiveAudioId)
const audioRef = useRef(null)

useEffect(() => {
if (activeAudioId && activeAudioId !== msg._id && audioRef.current) {
audioRef.current.pause()
}
}, [activeAudioId, msg._id])

const getTranslatedText = (text) => {
const translations = {
hello: "hola",
Expand Down Expand Up @@ -93,7 +103,7 @@ export default function MessageBubble({ msg, isMine, showTime, selectedUser, isO
src={msg.image}
alt="attachment"
className="max-w-full rounded-lg mb-1 cursor-pointer hover:opacity-90 transition"
onClick={() => setSelectedImage(msg.image)}
onClick={() => window.open(msg.image, "_blank")}
/>
)}

Expand All @@ -105,9 +115,21 @@ export default function MessageBubble({ msg, isMine, showTime, selectedUser, isO
</div>

<audio
ref={audioRef}
src={msg.audio}
controls
className="max-w-full h-10 mb-1"
onPlay={() => setActiveAudioId(msg._id)}
onPause={() => {
if (activeAudioId === msg._id) {
setActiveAudioId(null);
}
}}
onEnded={() => {
if (activeAudioId === msg._id) {
setActiveAudioId(null);
}
}}
/>
</>
)}
Expand All @@ -121,15 +143,11 @@ export default function MessageBubble({ msg, isMine, showTime, selectedUser, isO
</p>

<button
key={emoji}
title={`${count} reaction${count > 1 ? "s" : ""}`}
onClick={(e) => { e.stopPropagation(); onReact(msg._id, emoji); }}
className={`text-xs px-1.5 py-0.5 rounded-full shadow-sm hover:scale-110 hover:shadow-md transition-all ${isMine ? "bg-primary-focus text-primary-content border border-white/20" : "bg-base-100 text-base-content border border-base-300"}`}
>
<Languages className="w-3 h-3" />
{showTranslation
? "Show Original"
: "Translate"}
onClick={(e) => { e.stopPropagation(); setShowTranslation(!showTranslation); }}
className={`text-xs px-1.5 py-0.5 rounded-full shadow-sm hover:scale-110 hover:shadow-md transition-all ${isMine ? "bg-primary-focus text-primary-content border border-white/20" : "bg-base-100 text-base-content border border-base-300"}`}
>
<Languages className="w-3 h-3 inline mr-1" />
{showTranslation ? "Show Original" : "Translate"}
</button>

{msg.edited && (
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/store/useChatStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const useChatStore = create((set, get) => ({
isMessagesLoading: false,
hasMore: false,
isLoadingMore: false,
activeAudioId: null,

getUsers: async () => {
set({ isUsersLoading: true });
Expand Down Expand Up @@ -348,19 +349,22 @@ const useChatStore = create((set, get) => ({
},

setSelectedUser: (user) => {
if (!user) return set({ selectedUser: null, messages: [] });
if (!user) return set({ selectedUser: null, messages: [], activeAudioId: null });
const current = get().selectedUser;
if (current?._id === user?._id) return;

// Clear unread count for this user when selecting them
set((state) => ({
selectedUser: user,
messages: [],
activeAudioId: null,
users: state.users.map((u) =>
u._id === user._id ? { ...u, unreadCount: 0 } : u
),
}));
},

setActiveAudioId: (id) => set({ activeAudioId: id }),
}));

export default useChatStore;
Loading