From df394b3b7dc09fde624aa578c9dbe612dc3e91df Mon Sep 17 00:00:00 2001 From: DGSoft Date: Fri, 12 Dec 2025 12:45:03 +0100 Subject: [PATCH] Add 'read up to here' marker line in chat messages --- frontend/src/components/Chat/MessageList.tsx | 19 +++++++++++++++ .../src/contexts/UnreadMessagesContext.tsx | 24 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/frontend/src/components/Chat/MessageList.tsx b/frontend/src/components/Chat/MessageList.tsx index 7d66119..0636ffe 100644 --- a/frontend/src/components/Chat/MessageList.tsx +++ b/frontend/src/components/Chat/MessageList.tsx @@ -5,6 +5,7 @@ import CodeBlock from '../common/CodeBlock'; import { useAuth } from '../../contexts/AuthContext'; import { useToast } from '../../contexts/ToastContext'; import { useUserStatus } from '../../contexts/UserStatusContext'; +import { useUnreadMessages } from '../../contexts/UnreadMessagesContext'; import UserStatusIndicator from '../common/UserStatusIndicator'; interface MessageListProps { @@ -16,6 +17,7 @@ const MessageList: React.FC = ({ channelId, onReply }) => { const { user } = useAuth(); const { addToast } = useToast(); const { getUserStatus } = useUserStatus(); + const { getLastReadTimestamp } = useUnreadMessages(); const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(true); const [hasMore, setHasMore] = useState(true); @@ -272,6 +274,23 @@ const MessageList: React.FC = ({ channelId, onReply }) => { Lade ältere Nachrichten... )} + {(() => { + const lastReadTimestamp = getLastReadTimestamp('channel', channelId); + const hasUnreadMessages = messages.some(msg => { + const messageDate = new Date(msg.created_at); + return lastReadTimestamp && messageDate > lastReadTimestamp; + }); + + return hasUnreadMessages ? ( +
+
+
+ Gelesen bis hier +
+
+
+ ) : null; + })()} {messages.map((message) => { const isOwnMessage = user && message.sender_id === user.id; diff --git a/frontend/src/contexts/UnreadMessagesContext.tsx b/frontend/src/contexts/UnreadMessagesContext.tsx index 55bbf34..8905ca5 100644 --- a/frontend/src/contexts/UnreadMessagesContext.tsx +++ b/frontend/src/contexts/UnreadMessagesContext.tsx @@ -6,6 +6,7 @@ interface UnreadMessagesContextType { unreadDirectMessages: Set; activeChannelId: number | null; activeDirectMessageUserId: number | null; + lastReadTimestamps: { [key: string]: Date }; // 'channel-{id}' or 'dm-{userId}' => timestamp markChannelAsRead: (channelId: number) => void; markChannelAsUnread: (channelId: number) => void; markDirectMessageAsRead: (userId: number) => void; @@ -14,6 +15,7 @@ interface UnreadMessagesContextType { hasUnreadDirectMessage: (userId: number) => boolean; setActiveChannel: (channelId: number | null) => void; setActiveDirectMessage: (userId: number | null) => void; + getLastReadTimestamp: (type: 'channel' | 'dm', id: number) => Date | null; } const UnreadMessagesContext = createContext(undefined); @@ -35,6 +37,7 @@ export const UnreadMessagesProvider: React.FC = ({ const [unreadDirectMessages, setUnreadDirectMessages] = useState>(new Set()); const [activeChannelId, setActiveChannelId] = useState(null); const [activeDirectMessageUserId, setActiveDirectMessageUserId] = useState(null); + const [lastReadTimestamps, setLastReadTimestamps] = useState<{ [key: string]: Date }>({}); const { user } = useAuth(); // Listen for unread message events from the presence WebSocket @@ -87,10 +90,24 @@ export const UnreadMessagesProvider: React.FC = ({ const setActiveChannel = (channelId: number | null) => { setActiveChannelId(channelId); + if (channelId !== null) { + // Set timestamp when channel becomes active + setLastReadTimestamps(prev => ({ + ...prev, + [`channel-${channelId}`]: new Date() + })); + } }; const setActiveDirectMessage = (userId: number | null) => { setActiveDirectMessageUserId(userId); + if (userId !== null) { + // Set timestamp when DM becomes active + setLastReadTimestamps(prev => ({ + ...prev, + [`dm-${userId}`]: new Date() + })); + } }; const hasUnreadChannel = (channelId: number) => { @@ -101,6 +118,11 @@ export const UnreadMessagesProvider: React.FC = ({ return unreadDirectMessages.has(userId); }; + const getLastReadTimestamp = (type: 'channel' | 'dm', id: number): Date | null => { + const key = `${type}-${id}`; + return lastReadTimestamps[key] || null; + }; + return ( = ({ unreadDirectMessages, activeChannelId, activeDirectMessageUserId, + lastReadTimestamps, markChannelAsRead, markChannelAsUnread, markDirectMessageAsRead, @@ -116,6 +139,7 @@ export const UnreadMessagesProvider: React.FC = ({ hasUnreadDirectMessage, setActiveChannel, setActiveDirectMessage, + getLastReadTimestamp, }} > {children}