mirror of
https://github.com/OHV-IT/collabrix.git
synced 2025-12-16 00:58:37 +01:00
- Implement unread message indicators with Material-UI icons - Add BlinkingEnvelope component with theme-compatible colors - Create UnreadMessagesContext for managing unread states - Integrate WebSocket message handling for real-time notifications - Icons only appear for inactive channels/DMs, disappear when opened - Add test functionality (double-click to mark as unread) - Fix WebSocket URL handling for production deployment - Unify WebSocket architecture using presence connection for all messages
124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
|
|
import { useAuth } from './AuthContext';
|
|
|
|
interface UnreadMessagesContextType {
|
|
unreadChannels: Set<number>;
|
|
unreadDirectMessages: Set<number>;
|
|
activeChannelId: number | null;
|
|
activeDirectMessageUserId: number | null;
|
|
markChannelAsRead: (channelId: number) => void;
|
|
markChannelAsUnread: (channelId: number) => void;
|
|
markDirectMessageAsRead: (userId: number) => void;
|
|
markDirectMessageAsUnread: (userId: number) => void;
|
|
hasUnreadChannel: (channelId: number) => boolean;
|
|
hasUnreadDirectMessage: (userId: number) => boolean;
|
|
setActiveChannel: (channelId: number | null) => void;
|
|
setActiveDirectMessage: (userId: number | null) => void;
|
|
}
|
|
|
|
const UnreadMessagesContext = createContext<UnreadMessagesContextType | undefined>(undefined);
|
|
|
|
export const useUnreadMessages = () => {
|
|
const context = useContext(UnreadMessagesContext);
|
|
if (context === undefined) {
|
|
throw new Error('useUnreadMessages must be used within a UnreadMessagesProvider');
|
|
}
|
|
return context;
|
|
};
|
|
|
|
interface UnreadMessagesProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export const UnreadMessagesProvider: React.FC<UnreadMessagesProviderProps> = ({ children }) => {
|
|
const [unreadChannels, setUnreadChannels] = useState<Set<number>>(new Set());
|
|
const [unreadDirectMessages, setUnreadDirectMessages] = useState<Set<number>>(new Set());
|
|
const [activeChannelId, setActiveChannelId] = useState<number | null>(null);
|
|
const [activeDirectMessageUserId, setActiveDirectMessageUserId] = useState<number | null>(null);
|
|
const { user } = useAuth();
|
|
|
|
// Listen for unread message events from the presence WebSocket
|
|
useEffect(() => {
|
|
const handleUnreadMessage = (event: CustomEvent) => {
|
|
const data = event.detail;
|
|
if (data.type === 'message' && data.message) {
|
|
// Mark channel as unread if message is from another user and channel is not active
|
|
if (user && data.message.sender_id !== user.id && activeChannelId !== data.message.channel_id) {
|
|
markChannelAsUnread(data.message.channel_id);
|
|
}
|
|
} else if (data.type === 'direct_message' && data.message) {
|
|
// Mark DM as unread if message is from another user and DM is not active
|
|
if (user && data.message.sender_id !== user.id && activeDirectMessageUserId !== data.message.sender_id) {
|
|
markDirectMessageAsUnread(data.message.sender_id);
|
|
}
|
|
}
|
|
};
|
|
|
|
window.addEventListener('unreadMessage', handleUnreadMessage as EventListener);
|
|
|
|
return () => {
|
|
window.removeEventListener('unreadMessage', handleUnreadMessage as EventListener);
|
|
};
|
|
}, [user, activeChannelId, activeDirectMessageUserId]);
|
|
|
|
const markChannelAsRead = (channelId: number) => {
|
|
setUnreadChannels(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(channelId);
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
const markChannelAsUnread = (channelId: number) => {
|
|
setUnreadChannels(prev => new Set(prev).add(channelId));
|
|
};
|
|
|
|
const markDirectMessageAsRead = (userId: number) => {
|
|
setUnreadDirectMessages(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(userId);
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
const markDirectMessageAsUnread = (userId: number) => {
|
|
setUnreadDirectMessages(prev => new Set(prev).add(userId));
|
|
};
|
|
|
|
const setActiveChannel = (channelId: number | null) => {
|
|
setActiveChannelId(channelId);
|
|
};
|
|
|
|
const setActiveDirectMessage = (userId: number | null) => {
|
|
setActiveDirectMessageUserId(userId);
|
|
};
|
|
|
|
const hasUnreadChannel = (channelId: number) => {
|
|
return unreadChannels.has(channelId);
|
|
};
|
|
|
|
const hasUnreadDirectMessage = (userId: number) => {
|
|
return unreadDirectMessages.has(userId);
|
|
};
|
|
|
|
return (
|
|
<UnreadMessagesContext.Provider
|
|
value={{
|
|
unreadChannels,
|
|
unreadDirectMessages,
|
|
activeChannelId,
|
|
activeDirectMessageUserId,
|
|
markChannelAsRead,
|
|
markChannelAsUnread,
|
|
markDirectMessageAsRead,
|
|
markDirectMessageAsUnread,
|
|
hasUnreadChannel,
|
|
hasUnreadDirectMessage,
|
|
setActiveChannel,
|
|
setActiveDirectMessage,
|
|
}}
|
|
>
|
|
{children}
|
|
</UnreadMessagesContext.Provider>
|
|
);
|
|
}; |