diff --git a/Frontend/Electron/package.json b/Frontend/Electron/package.json index 22aa83e..5bf9d1d 100644 --- a/Frontend/Electron/package.json +++ b/Frontend/Electron/package.json @@ -1,7 +1,7 @@ { "name": "discord", "private": true, - "version": "1.0.3", + "version": "1.0.4", "description": "A Discord clone built with Convex, React, and Electron", "author": "Moyettes", "type": "module", diff --git a/Frontend/Electron/src/assets/icons/friends.svg b/Frontend/Electron/src/assets/icons/friends.svg new file mode 100644 index 0000000..39eaef5 --- /dev/null +++ b/Frontend/Electron/src/assets/icons/friends.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Frontend/Electron/src/assets/icons/index.js b/Frontend/Electron/src/assets/icons/index.js index 4a0c10e..90e4042 100644 --- a/Frontend/Electron/src/assets/icons/index.js +++ b/Frontend/Electron/src/assets/icons/index.js @@ -24,6 +24,7 @@ import TypingIcon from './typing.svg'; import DMIcon from './dm.svg'; import SpoilerIcon from './spoiler.svg'; import CrownIcon from './crown.svg'; +import FriendsIcon from './friends.svg'; export { AddIcon, @@ -51,7 +52,8 @@ export { TypingIcon, DMIcon, SpoilerIcon, - CrownIcon + CrownIcon, + FriendsIcon }; export const Icons = { @@ -80,5 +82,6 @@ export const Icons = { Typing: TypingIcon, DM: DMIcon, Spoiler: SpoilerIcon, - Crown: CrownIcon + Crown: CrownIcon, + Friends: FriendsIcon }; diff --git a/Frontend/Electron/src/components/ChatArea.jsx b/Frontend/Electron/src/components/ChatArea.jsx index 974dafd..07ba3bc 100644 --- a/Frontend/Electron/src/components/ChatArea.jsx +++ b/Frontend/Electron/src/components/ChatArea.jsx @@ -692,10 +692,13 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u if (!currentUserId || !channelId || !decryptedMessages.length) return; const lastMsg = decryptedMessages[decryptedMessages.length - 1]; if (!lastMsg?.created_at) return; - markReadMutation({ userId: currentUserId, channelId, lastReadTimestamp: lastMsg.created_at }).catch(() => {}); + markReadMutation({ userId: currentUserId, channelId, lastReadTimestamp: new Date(lastMsg.created_at).getTime() }).catch(() => {}); setUnreadDividerTimestamp(null); }, [currentUserId, channelId, decryptedMessages, markReadMutation]); + const markChannelAsReadRef = useRef(markChannelAsRead); + markChannelAsReadRef.current = markChannelAsRead; + const typingUsers = typingData.filter(t => t.username !== username); const filteredMentionMembers = mentionQuery !== null ? filterMembersForMention(members, mentionQuery) : []; @@ -775,7 +778,7 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u // Mark as read on initial load (already scrolled to bottom) useEffect(() => { - if (!isInitialLoadRef.current && decryptedMessages.length > 0) { + if (decryptedMessages.length > 0) { const container = messagesContainerRef.current; if (!container) return; const { scrollTop, scrollHeight, clientHeight } = container; @@ -785,6 +788,13 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u } }, [decryptedMessages.length, markChannelAsRead]); + // Mark as read when component unmounts (e.g., switching to voice channel) + useEffect(() => { + return () => { + markChannelAsReadRef.current(); + }; + }, []); + const saveSelection = () => { const sel = window.getSelection(); if (sel.rangeCount > 0) savedRangeRef.current = sel.getRangeAt(0).cloneRange(); diff --git a/Frontend/Electron/src/components/DMList.jsx b/Frontend/Electron/src/components/DMList.jsx index 6acad46..83b3d72 100644 --- a/Frontend/Electron/src/components/DMList.jsx +++ b/Frontend/Electron/src/components/DMList.jsx @@ -4,6 +4,7 @@ import { api } from '../../../../convex/_generated/api'; import Tooltip from './Tooltip'; import Avatar from './Avatar'; import { useOnlineUsers } from '../contexts/PresenceContext'; +import friendsIcon from '../assets/icons/friends.svg'; const STATUS_COLORS = { online: '#3ba55c', @@ -86,7 +87,7 @@ const DMList = ({ dmChannels, activeDMChannel, onSelectDM, onOpenDM }) => { }; return ( -