'init'
This commit is contained in:
170
app/components/GroupDialog/GroupDialog.tsx
Normal file
170
app/components/GroupDialog/GroupDialog.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
||||
import { Avatar, Badge, Box, Divider, Flex, Loader, Skeleton, Text, useComputedColorScheme, useMantineTheme } from "@mantine/core";
|
||||
import { IconAlertCircle, IconBellOff, IconCheck, IconChecks, IconClock, IconPin, IconUsers } from "@tabler/icons-react";
|
||||
import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider";
|
||||
import { dotMessageIfNeeded, isMessageDeliveredByTime } from "@/app/utils/utils";
|
||||
import { usePacket } from "@/app/providers/ProtocolProvider/usePacket";
|
||||
import { useEffect, useState } from "react";
|
||||
import { PacketTyping } from "@/app/providers/ProtocolProvider/protocol/packets/packet.typeing";
|
||||
import { useAvatars } from "@/app/providers/AvatarProvider/useAvatars";
|
||||
import { TextParser } from "../TextParser/TextParser";
|
||||
import { useMemory } from "@/app/providers/MemoryProvider/useMemory";
|
||||
import { DialogRow } from "@/app/providers/DialogListProvider/DialogListProvider";
|
||||
import { useGroupInformation } from "@/app/providers/InformationProvider/useGroupInformation";
|
||||
import { useDialogInfo } from "@/app/providers/DialogListProvider/useDialogInfo";
|
||||
import { useUserInformation } from "@/app/providers/InformationProvider/useUserInformation";
|
||||
import { useDialogContextMenu } from "@/app/hooks/useDialogContextMenu";
|
||||
import { useDialogMute } from "@/app/providers/DialogStateProvider.tsx/useDialogMute";
|
||||
import { useDialogPin } from "@/app/providers/DialogStateProvider.tsx/useDialogPin";
|
||||
import { useMentions } from "@/app/providers/DialogStateProvider.tsx/useMentions";
|
||||
|
||||
export interface DialogProps extends DialogRow {
|
||||
onClickDialog: (dialog: string) => void;
|
||||
}
|
||||
|
||||
export function GroupDialog(props : DialogProps) {
|
||||
const colors = useRosettaColors();
|
||||
const theme = useMantineTheme();
|
||||
const computedTheme = useComputedColorScheme();
|
||||
|
||||
/**
|
||||
* Принимает #group:group_id, для
|
||||
* диалогов между пользователями есть просто public_key собеседника
|
||||
*/
|
||||
const groupId = props.dialog_id;
|
||||
const {isMuted} = useDialogMute(groupId);
|
||||
const {isPinned} = useDialogPin(groupId);
|
||||
|
||||
const {groupInfo} = useGroupInformation(groupId);
|
||||
|
||||
const {lastMessage, unreaded, loading} = useDialogInfo(props);
|
||||
const lastMessageFromMe = lastMessage.from_me == 1;
|
||||
|
||||
const [usersTypeing, setUsersTypeing] = useState<{
|
||||
timeout: NodeJS.Timeout | null,
|
||||
fromPublicKey: string
|
||||
}[]>([]);
|
||||
|
||||
const avatars = useAvatars(groupId);
|
||||
const [сurrentDialogPublicKeyView] = useMemory("current-dialog-public-key-view", "", true);
|
||||
const [userInfo] = useUserInformation(lastMessage.from_public_key);
|
||||
const [typingUser] = useUserInformation(usersTypeing[0]?.fromPublicKey || '');
|
||||
|
||||
const isInCurrentDialog = props.dialog_id == сurrentDialogPublicKeyView;
|
||||
const currentDialogColor = computedTheme == 'dark' ? '#2a6292' :'#438fd1';
|
||||
const {openContextMenu} = useDialogContextMenu();
|
||||
const {isMentioned} = useMentions();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
clearUsersTypeing();
|
||||
}, [props.dialog_id]);
|
||||
|
||||
const clearUsersTypeing = () => {
|
||||
usersTypeing.forEach(ut => {
|
||||
if(ut.timeout){
|
||||
clearTimeout(ut.timeout);
|
||||
}
|
||||
});
|
||||
setUsersTypeing([]);
|
||||
}
|
||||
|
||||
usePacket(0x0B, (packet : PacketTyping) => {
|
||||
if(packet.getToPublicKey() == props.dialog_id){
|
||||
setUsersTypeing((prev) => [...prev, {
|
||||
fromPublicKey: packet.getFromPublicKey(),
|
||||
timeout: setTimeout(() => {
|
||||
setUsersTypeing((prev) => {
|
||||
return prev.filter(ut => ut.fromPublicKey != packet.getFromPublicKey());
|
||||
});
|
||||
}, 3000)
|
||||
}]);
|
||||
}
|
||||
}, [props.dialog_id]);
|
||||
|
||||
return (
|
||||
<Box style={{
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
backgroundColor: isInCurrentDialog ? currentDialogColor : 'unset',
|
||||
}} onClick={() => props.onClickDialog(props.dialog_id)} onContextMenu={() => {
|
||||
openContextMenu(props.dialog_id)
|
||||
}}>
|
||||
<Flex p={'sm'} gap={'sm'}>
|
||||
<Box style={{ position: 'relative', display: 'inline-block' }}>
|
||||
<Avatar src={avatars.length > 0 ? avatars[0].avatar : undefined} variant={isInCurrentDialog ? 'filled' : 'light'} name={groupInfo.title} size={50} color={'initials'} />
|
||||
</Box>
|
||||
<Flex w={'100%'} justify={'space-between'} direction={'row'} gap={'sm'}>
|
||||
<Flex direction={'column'} gap={3}>
|
||||
<Flex align={'center'} gap={5}>
|
||||
<Text size={'sm'} c={computedTheme == 'light' && !isInCurrentDialog ? 'black' : 'white'} fw={500}>
|
||||
{dotMessageIfNeeded(groupInfo.title, 15)}
|
||||
</Text>
|
||||
<IconUsers color={isInCurrentDialog ? '#fff' : colors.chevrons.active} size={13}></IconUsers>
|
||||
{isMuted && <IconBellOff color={isInCurrentDialog ? '#fff' : colors.chevrons.active} size={13}></IconBellOff>}
|
||||
{isPinned && <IconPin color={isInCurrentDialog ? '#fff' : colors.chevrons.active} size={13}></IconPin>}
|
||||
</Flex>
|
||||
{usersTypeing.length <= 0 && <>
|
||||
<Text component="div" c={
|
||||
isInCurrentDialog ? '#fff' : colors.chevrons.active
|
||||
} size={'xs'} style={{
|
||||
maxWidth: '130px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}>
|
||||
{loading && <Skeleton height={10} mt={4} width={100}></Skeleton>}
|
||||
{!loading && (
|
||||
<>
|
||||
<span style={{
|
||||
color: isInCurrentDialog ? '#fff' : theme.colors.blue[6]
|
||||
}}>{userInfo.title}: </span>
|
||||
<TextParser
|
||||
__reserved_1={isInCurrentDialog}
|
||||
noHydrate={true}
|
||||
text={lastMessage.plain_message}
|
||||
></TextParser>
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
</>}
|
||||
{usersTypeing.length > 0 && <>
|
||||
<Flex gap={5} align={'center'}>
|
||||
<Text c={isInCurrentDialog ? '#fff' : theme.colors.blue[3]} fz={12}>{typingUser.title} {usersTypeing.length > 1 && 'and ' + (usersTypeing.length - 1)} typing </Text>
|
||||
<Loader size={15} color={isInCurrentDialog ? '#fff' : theme.colors.blue[3]} type={'dots'}></Loader>
|
||||
</Flex>
|
||||
</>}
|
||||
</Flex>
|
||||
<Flex direction={'column'} align={'flex-end'} gap={8}>
|
||||
{!loading && (
|
||||
<Text c={сurrentDialogPublicKeyView == props.dialog_id ? '#fff' : colors.chevrons.active} fz={10}>
|
||||
{new Date(lastMessage.timestamp).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}
|
||||
</Text>
|
||||
)}
|
||||
{loading && (
|
||||
<Skeleton height={8} mt={4} width={30}></Skeleton>
|
||||
)}
|
||||
{lastMessage.delivered == DeliveredMessageState.DELIVERED && <>
|
||||
{lastMessageFromMe && unreaded > 0 &&
|
||||
<IconCheck stroke={3} color={isInCurrentDialog ? '#fff' : theme.colors.blue[3]} size={14}></IconCheck>}
|
||||
{lastMessageFromMe && unreaded <= 0 &&
|
||||
<IconChecks stroke={3} color={isInCurrentDialog ? '#fff' : theme.colors.blue[3]} size={14}></IconChecks>}
|
||||
</>}
|
||||
{(lastMessage.delivered == DeliveredMessageState.WAITING && (isMessageDeliveredByTime(lastMessage.timestamp, lastMessage.attachments.length))) && <>
|
||||
<IconClock stroke={2} size={13} color={theme.colors.gray[5]}></IconClock>
|
||||
</>}
|
||||
{!loading && (lastMessage.delivered == DeliveredMessageState.ERROR || (!isMessageDeliveredByTime(lastMessage.timestamp, lastMessage.attachments.length) && lastMessage.delivered != DeliveredMessageState.DELIVERED)) && (
|
||||
<IconAlertCircle stroke={3} size={15} color={colors.error}></IconAlertCircle>
|
||||
)}
|
||||
{unreaded > 0 && !lastMessageFromMe && !isMentioned(props.dialog_id) && <Badge
|
||||
color={isInCurrentDialog ? 'white' : (isMuted ? colors.chevrons.active : colors.brandColor)}
|
||||
c={isInCurrentDialog ? colors.brandColor : 'white'}
|
||||
size={'sm'} circle={unreaded < 10}>{unreaded > 99 ? '99+' : unreaded}</Badge>}
|
||||
{isMentioned(props.dialog_id) && !lastMessageFromMe && <Badge size={'sm'} circle c={isInCurrentDialog ? colors.brandColor : 'white'} color={isInCurrentDialog ? 'white' : colors.brandColor}>@</Badge>}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Divider></Divider>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user