Files
desktop/app/components/GroupHeader/GroupHeader.tsx
rosetta 83f38dc63f 'init'
2026-01-30 05:01:05 +02:00

152 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
import { useDialog } from "@/app/providers/DialogProvider/useDialog";
import { ProtocolState } from "@/app/providers/ProtocolProvider/ProtocolProvider";
import { useProtocolState } from "@/app/providers/ProtocolProvider/useProtocolState";
import { Avatar, Box, Divider, Flex, Loader, Skeleton, Text, Tooltip, useComputedColorScheme, useMantineTheme } from "@mantine/core";
import { modals } from "@mantine/modals";
import { IconTrashX } from "@tabler/icons-react";
import { useEffect, useState } from "react";
import { usePacket } from "@/app/providers/ProtocolProvider/usePacket";
import { PacketTyping } from "@/app/providers/ProtocolProvider/protocol/packets/packet.typeing";
import { useAvatars } from "@/app/providers/AvatarProvider/useAvatars";
import { useReplyMessages } from "@/app/providers/DialogProvider/useReplyMessages";
import { ReplyHeader } from "../ReplyHeader/ReplyHeader";
import { useRosettaBreakpoints } from "@/app/hooks/useRosettaBreakpoints";
import { BackToDialogs } from "../BackToDialogs/BackToDialogs";
import { useGroupInformation } from "@/app/providers/InformationProvider/useGroupInformation";
import { useNavigate } from "react-router-dom";
import { useGroupMembers } from "@/app/providers/InformationProvider/useGroupMembers";
import { useUserInformation } from "@/app/providers/InformationProvider/useUserInformation";
export function GroupHeader() {
const colors = useRosettaColors();
const computedTheme = useComputedColorScheme();
const {deleteMessages, dialog} = useDialog();
const theme = useMantineTheme();
const {groupInfo} = useGroupInformation(dialog);
const protocolState = useProtocolState();
const [usersTypeing, setUsersTypeing] = useState<{
timeout: NodeJS.Timeout | null,
fromPublicKey: string
}[]>([]);
const avatars = useAvatars(dialog);
const {replyMessages} = useReplyMessages();
const {lg} = useRosettaBreakpoints();
const [userInfo] = useUserInformation(usersTypeing[0]?.fromPublicKey || '');
const navigate = useNavigate();
/**
* Указывем force для того, чтобы при открытии диалога
* с группой подгружался сразу актуальный список участников
* даже если он уже был загружен ранее. Потому что
* событие добавления/удаления участников могло произойти
* когда диалог был закрыт.
*/
const {members, loading} = useGroupMembers(groupInfo.groupId, true);
useEffect(() => {
clearUsersTypeing();
}, [dialog]);
const clearUsersTypeing = () => {
usersTypeing.forEach(ut => {
if(ut.timeout){
clearTimeout(ut.timeout);
}
});
setUsersTypeing([]);
}
usePacket(0x0B, (packet : PacketTyping) => {
if(packet.getToPublicKey() == dialog){
setUsersTypeing((prev) => [...prev, {
fromPublicKey: packet.getFromPublicKey(),
timeout: setTimeout(() => {
setUsersTypeing((prev) => {
return prev.filter(ut => ut.fromPublicKey != packet.getFromPublicKey());
});
}, 3000)
}]);
}
}, [dialog]);
const clearMessages = async () => {
deleteMessages();
modals.closeAll();
}
const onClickClearMessages = () => {
modals.openConfirmModal({
title: 'Clear all messages?',
centered: true,
children: (
<Text size="sm">
Are you sure you want to clear all messages? This action cannot be undone.
</Text>
),
withCloseButton: false,
labels: { confirm: 'Continue', cancel: "Cancel" },
confirmProps: { color: 'red' },
onConfirm: clearMessages
});
}
const onClickProfile = () => {
navigate(`/main/group/${groupInfo.groupId.replace('#group:', '')}`);
}
return (<>
<Box bg={colors.boxColor} style={{
userSelect: 'none',
}} h={60}>
{(replyMessages.messages.length <= 0 || replyMessages.inDialogInput) && <Flex p={'sm'} h={'100%'} justify={'space-between'} align={'center'} gap={'sm'}>
<Flex style={{
cursor: 'pointer'
}} h={'100%'} align={'center'} gap={'sm'}>
{!lg && <BackToDialogs></BackToDialogs>}
<Avatar onClick={onClickProfile} color={'initials'} src={avatars.length > 0 ? avatars[0].avatar : undefined} name={groupInfo.title}></Avatar>
<Flex direction={'column'} onClick={onClickProfile}>
<Flex align={'center'} gap={3}>
<Text size={'sm'} c={computedTheme == 'light' ? 'black' : 'white'} fw={500}>
{groupInfo.title}
</Text>
</Flex>
{members.length > 0 && usersTypeing.length <= 0 && protocolState == ProtocolState.CONNECTED && (
<Text c={theme.colors.gray[5]} fz={12}>{members.length} member{members.length > 1 ? 's' : ''}</Text>
)}
{loading && usersTypeing.length <= 0 && protocolState == ProtocolState.CONNECTED && members.length == 0 && (
<Skeleton height={12} mt={7} width={80} radius="xl" />
)}
{!loading && members.length == 0 && (
<Text c={theme.colors.gray[5]} fz={12}>
Deleted group
</Text>
)}
{usersTypeing.length > 0 && protocolState == ProtocolState.CONNECTED && <>
<Flex gap={5} align={'center'}>
<Text c={theme.colors.blue[3]} fz={12}>{userInfo.title} {usersTypeing.length > 1 && 'and ' + (usersTypeing.length - 1)} typing </Text>
<Loader size={15} color={theme.colors.blue[3]} type={'dots'}></Loader>
</Flex>
</>}
{protocolState != ProtocolState.CONNECTED &&
<Flex gap={'xs'} align={'center'}>
<Loader size={8} color={colors.chevrons.active}></Loader>
<Text c={theme.colors.gray[5]} fz={12}>connecting...</Text>
</Flex>
}
</Flex>
</Flex>
<Flex h={'100%'} align={'center'} gap={'sm'}>
<Tooltip onClick={onClickClearMessages} withArrow position={'bottom'} label={"Clear all messages"}>
<IconTrashX
style={{
cursor: 'pointer'
}} stroke={1.5} color={theme.colors.blue[7]} size={24}></IconTrashX>
</Tooltip>
</Flex>
</Flex>}
{replyMessages.messages.length > 0 && !replyMessages.inDialogInput && <ReplyHeader></ReplyHeader>}
</Box>
<Divider color={colors.borderColor}></Divider>
</>)
}