110 lines
3.8 KiB
TypeScript
110 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
|
|
import { useUser } from "@/contexts/UserContext";
|
|
import {
|
|
type ChatIdentity,
|
|
getChatLastMessagePreview,
|
|
getChatTitle,
|
|
isDepartmentCommentsChat,
|
|
} from "@/lib/messages/chatUtils";
|
|
import type { Chat } from "@/types/api";
|
|
import { useMessagesChatList } from "@/hooks/useMessagesChatList";
|
|
import { useMessagesListFilters } from "@/hooks/useMessagesListFilters";
|
|
|
|
export type MessageChatTypeCounts = Record<string, { total: number; unread: number }>;
|
|
|
|
function chatTimestamp(chat: Chat): number {
|
|
const lastMessage = chat.last_message;
|
|
if (typeof lastMessage?.created_ts === "number") return lastMessage.created_ts;
|
|
|
|
const raw = lastMessage?.created_at || lastMessage?.created || chat.created_at;
|
|
const timestamp = new Date(raw).getTime();
|
|
return Number.isNaN(timestamp) ? 0 : timestamp;
|
|
}
|
|
|
|
export function useMessagesDirectory() {
|
|
const { user } = useUser();
|
|
const { search, chatTypeFilter, filterUnread, filterPinned } = useMessagesListFilters(user?.id);
|
|
const { chats: allChats, loading, error, refreshChat } = useMessagesChatList(user?.id);
|
|
|
|
const identity = useMemo<ChatIdentity>(() => ({
|
|
first_name: user?.first_name,
|
|
last_name: user?.last_name,
|
|
patronymic: user?.patronymic,
|
|
email: user?.email,
|
|
}), [user?.email, user?.first_name, user?.last_name, user?.patronymic]);
|
|
|
|
const departmentIds = useMemo(
|
|
() => new Set((user?.departments || []).map((department) => department.id)),
|
|
[user?.departments],
|
|
);
|
|
|
|
const chats = useMemo(
|
|
() => allChats.filter((chat) => {
|
|
const type = chat.chat_type || chat.type;
|
|
if (type !== "comments") return true;
|
|
return isDepartmentCommentsChat(chat)
|
|
&& typeof chat.context_object_id === "number"
|
|
&& departmentIds.has(chat.context_object_id);
|
|
}),
|
|
[allChats, departmentIds],
|
|
);
|
|
|
|
const visibleChats = useMemo(() => {
|
|
const query = search.trim().toLowerCase();
|
|
return [...chats]
|
|
.sort((a, b) => Number(Boolean(b.is_pinned)) - Number(Boolean(a.is_pinned)) || chatTimestamp(b) - chatTimestamp(a))
|
|
.filter((chat) => {
|
|
const type = chat.chat_type || chat.type;
|
|
const unread = chat.unread_count || 0;
|
|
if (chatTypeFilter !== "all" && type !== chatTypeFilter) return false;
|
|
if (filterUnread === "unread" && unread === 0) return false;
|
|
if (filterUnread === "read" && unread > 0) return false;
|
|
if (filterPinned === "pinned" && !chat.is_pinned) return false;
|
|
if (filterPinned === "unpinned" && chat.is_pinned) return false;
|
|
if (!query) return true;
|
|
|
|
const title = getChatTitle(chat, user?.id, identity).toLowerCase();
|
|
const lastMessagePreview = chat.last_message
|
|
? getChatLastMessagePreview(chat).toLowerCase()
|
|
: "";
|
|
return title.includes(query) || lastMessagePreview.includes(query);
|
|
});
|
|
}, [chatTypeFilter, chats, filterPinned, filterUnread, identity, search, user?.id]);
|
|
|
|
const chatTypeCounts = useMemo<MessageChatTypeCounts>(() => {
|
|
const counts: MessageChatTypeCounts = {
|
|
all: { total: chats.length, unread: 0 },
|
|
global: { total: 0, unread: 0 },
|
|
channel: { total: 0, unread: 0 },
|
|
comments: { total: 0, unread: 0 },
|
|
private: { total: 0, unread: 0 },
|
|
group: { total: 0, unread: 0 },
|
|
announcement: { total: 0, unread: 0 },
|
|
};
|
|
|
|
chats.forEach((chat) => {
|
|
const type = chat.chat_type || chat.type;
|
|
const unread = chat.unread_count || 0;
|
|
if (unread > 0) counts.all.unread += unread;
|
|
if (!type || !counts[type]) return;
|
|
counts[type].total += 1;
|
|
if (unread > 0) counts[type].unread += unread;
|
|
});
|
|
|
|
return counts;
|
|
}, [chats]);
|
|
|
|
return {
|
|
user,
|
|
identity,
|
|
visibleChats,
|
|
chatTypeCounts,
|
|
loading,
|
|
error,
|
|
refreshChat,
|
|
};
|
|
}
|