mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: added chat window layout
This commit is contained in:
parent
682cbc6ce9
commit
464f58363b
10
src/components/Chat/ChatContextProvider.tsx
Normal file
10
src/components/Chat/ChatContextProvider.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { ChatContext } from './context';
|
||||||
|
|
||||||
|
const ChatContextProvider: React.FC = ({ children }) => {
|
||||||
|
const [search, setSearch] = useState<string>('');
|
||||||
|
|
||||||
|
return <ChatContext.Provider value={{ search, setSearch }}>{children}</ChatContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ChatContextProvider };
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { GridItem, GridItemProps } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const Sidebar: React.FC<GridItemProps> = props => {
|
|
||||||
return (
|
|
||||||
<GridItem pt='7rem' borderRightWidth={'1px'} width='400px' pr='6' {...props}>
|
|
||||||
{props.children}
|
|
||||||
</GridItem>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Sidebar };
|
|
||||||
50
src/components/Chat/Sidebar/Heading.tsx
Normal file
50
src/components/Chat/Sidebar/Heading.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Heading as ChakraHeading,
|
||||||
|
Input,
|
||||||
|
InputGroup,
|
||||||
|
InputLeftElement,
|
||||||
|
HStack,
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
BoxProps,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { SearchIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useChatContext } from '../context';
|
||||||
|
|
||||||
|
const Heading: React.FC<BoxProps> = props => {
|
||||||
|
const { search, setSearch } = useChatContext();
|
||||||
|
|
||||||
|
const onSearch = (e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.target.value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box {...props}>
|
||||||
|
<Flex my='4' pr={'3'} justifyContent={'space-between'} alignItems={'center'}>
|
||||||
|
<ChakraHeading as='h2' fontSize={'md'} fontWeight='medium' mr='9'>
|
||||||
|
Chats
|
||||||
|
</ChakraHeading>
|
||||||
|
<InputGroup maxW='230px' size='sm'>
|
||||||
|
<InputLeftElement pointerEvents='none'>
|
||||||
|
<SearchIcon color='gray.300' />
|
||||||
|
</InputLeftElement>
|
||||||
|
<Input type='text' onChange={onSearch} value={search} placeholder='Search by name...' borderRadius={'md'} />
|
||||||
|
</InputGroup>
|
||||||
|
</Flex>
|
||||||
|
<HStack>
|
||||||
|
<Button isActive variant={'ghost'} size='sm'>
|
||||||
|
All
|
||||||
|
</Button>
|
||||||
|
<Button colorScheme={'blue'} variant='ghost' size='sm'>
|
||||||
|
Unread
|
||||||
|
</Button>
|
||||||
|
</HStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Heading };
|
||||||
30
src/components/Chat/Sidebar/Sidebar.tsx
Normal file
30
src/components/Chat/Sidebar/Sidebar.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { BoxProps, GridItem, GridItemProps } from '@chakra-ui/react';
|
||||||
|
import { Heading } from './Heading';
|
||||||
|
import { Users } from './Users/Users';
|
||||||
|
|
||||||
|
export interface ISidebarComposition {
|
||||||
|
Heading: React.FC<BoxProps>;
|
||||||
|
Users: React.FC;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Sidebar: React.FC<GridItemProps> & ISidebarComposition = props => {
|
||||||
|
return (
|
||||||
|
<GridItem
|
||||||
|
pt='7rem'
|
||||||
|
borderRightWidth={{ base: '0', lg: '1px' }}
|
||||||
|
width={{ base: 'full', lg: '400px' }}
|
||||||
|
px={{ base: '0', lg: '3' }}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</GridItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sidebar.Heading = Heading;
|
||||||
|
Sidebar.Users = Users;
|
||||||
|
|
||||||
|
export { Sidebar };
|
||||||
21
src/components/Chat/Sidebar/Users/User/Avatar.tsx
Normal file
21
src/components/Chat/Sidebar/Users/User/Avatar.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Avatar as ChakraAvatar, AvatarBadge, WrapItem } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export interface IAvatarProps {
|
||||||
|
fullname: string;
|
||||||
|
isOnline: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Avatar: React.FC<IAvatarProps> = ({ fullname: name, isOnline }) => {
|
||||||
|
const onlineBg = isOnline ? 'green.500' : 'red.500';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WrapItem>
|
||||||
|
<ChakraAvatar name={name} size='md'>
|
||||||
|
<AvatarBadge boxSize='0.90em' bg={onlineBg} />
|
||||||
|
</ChakraAvatar>
|
||||||
|
</WrapItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Avatar };
|
||||||
22
src/components/Chat/Sidebar/Users/User/Info.tsx
Normal file
22
src/components/Chat/Sidebar/Users/User/Info.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export interface IInfoProps {
|
||||||
|
fullname: string;
|
||||||
|
lastMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Info: React.FC<IInfoProps> = ({ fullname, lastMessage }) => {
|
||||||
|
return (
|
||||||
|
<Box ml='4'>
|
||||||
|
<Text fontWeight={'semibold'}>
|
||||||
|
{fullname} <br />
|
||||||
|
<Text as={'span'} fontWeight='normal' fontSize={'sm'} color='gray.500'>
|
||||||
|
{lastMessage}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Info };
|
||||||
18
src/components/Chat/Sidebar/Users/User/Time.tsx
Normal file
18
src/components/Chat/Sidebar/Users/User/Time.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export interface ITimeProps {
|
||||||
|
time: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Time: React.FC<ITimeProps> = ({ time }) => {
|
||||||
|
return (
|
||||||
|
<Box alignSelf={'flex-end'} ml='auto'>
|
||||||
|
<Text as='span' color='gray.500'>
|
||||||
|
{time}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Time };
|
||||||
55
src/components/Chat/Sidebar/Users/User/User.tsx
Normal file
55
src/components/Chat/Sidebar/Users/User/User.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
|
import { IInfoProps, Info } from './Info';
|
||||||
|
import { ITimeProps, Time } from './Time';
|
||||||
|
import { IAvatarProps, Avatar } from './Avatar';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
|
import { useColorModeValue } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
interface IUserComposition {
|
||||||
|
Avatar: React.FC<IAvatarProps>;
|
||||||
|
Info: React.FC<IInfoProps>;
|
||||||
|
Time: React.FC<ITimeProps>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUserProps {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const User: React.FC<IUserProps> & IUserComposition = props => {
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
const chatId = new URLSearchParams(location.search).get('chat_id');
|
||||||
|
const bg = useColorModeValue('gray.50', 'gray.700');
|
||||||
|
|
||||||
|
const handleChat = () => {
|
||||||
|
history.replace({ pathname: location.pathname, search: new URLSearchParams({ ['chat_id']: props.id }).toString() });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
onClick={handleChat}
|
||||||
|
py='5'
|
||||||
|
px='4'
|
||||||
|
borderRadius={'lg'}
|
||||||
|
bg={chatId === props.id ? bg : ''}
|
||||||
|
_hover={{ bg }}
|
||||||
|
transition='all .1s'
|
||||||
|
cursor={'pointer'}
|
||||||
|
>
|
||||||
|
<Flex flexDir={'row'} alignItems={'center'}>
|
||||||
|
{props.children}
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.Avatar = Avatar;
|
||||||
|
User.Info = Info;
|
||||||
|
User.Time = Time;
|
||||||
|
|
||||||
|
export { User };
|
||||||
1
src/components/Chat/Sidebar/Users/User/index.ts
Normal file
1
src/components/Chat/Sidebar/Users/User/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { User } from './User';
|
||||||
39
src/components/Chat/Sidebar/Users/Users.tsx
Normal file
39
src/components/Chat/Sidebar/Users/Users.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Stack, Text } from '@chakra-ui/react';
|
||||||
|
import { User } from './User';
|
||||||
|
import { useChatContext } from '../../context';
|
||||||
|
|
||||||
|
const tempDb = [
|
||||||
|
{ id: 'user-1', name: 'Trace Kerluke', lastMessage: 'Hello.', time: '2g' },
|
||||||
|
{ id: 'user-2', name: 'Dalton Conn ', lastMessage: 'Hello, how you doing ?', time: '30m' },
|
||||||
|
{ id: 'user-3', name: 'Cleo Hane', lastMessage: 'Hi.', time: '1h' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const Users: React.FC = () => {
|
||||||
|
const { search } = useChatContext();
|
||||||
|
|
||||||
|
const filtered = !search ? tempDb : tempDb.filter(u => u.name.toLocaleLowerCase().includes(search));
|
||||||
|
const isEmpty = filtered.length === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack mt='12' maxH='690px' overflowY={'scroll'} pr={{ base: '0', lg: '3' }}>
|
||||||
|
{!isEmpty ? (
|
||||||
|
filtered.map(u => (
|
||||||
|
<User key={u.id} id={u.id}>
|
||||||
|
<User.Avatar isOnline={false} fullname={u.name} />
|
||||||
|
<User.Info fullname={u.name} lastMessage={u.lastMessage} />
|
||||||
|
<User.Time time={u.time} />
|
||||||
|
</User>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Box textAlign={'center'}>
|
||||||
|
<Text userSelect={'none'} color='gray.500'>
|
||||||
|
Cannot find any chat
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Users };
|
||||||
1
src/components/Chat/Sidebar/Users/index.ts
Normal file
1
src/components/Chat/Sidebar/Users/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { Users } from './Users';
|
||||||
1
src/components/Chat/Sidebar/index.ts
Normal file
1
src/components/Chat/Sidebar/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { Sidebar } from './Sidebar';
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { GridItem, GridItemProps } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const Window: React.FC<GridItemProps> = props => {
|
|
||||||
return <GridItem {...props}>{props.children}</GridItem>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Window };
|
|
||||||
16
src/components/Chat/Window/Message/Avatar.tsx
Normal file
16
src/components/Chat/Window/Message/Avatar.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { WrapItem, Avatar as ChakraAvatar } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export interface IAvatarProps {
|
||||||
|
fullname: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Avatar: React.FC<IAvatarProps> = ({ fullname: name }) => {
|
||||||
|
return (
|
||||||
|
<WrapItem>
|
||||||
|
<ChakraAvatar name={name} size={'sm'} />
|
||||||
|
</WrapItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Avatar };
|
||||||
29
src/components/Chat/Window/Message/Message.tsx
Normal file
29
src/components/Chat/Window/Message/Message.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Flex } from '@chakra-ui/react';
|
||||||
|
import { Avatar, IAvatarProps } from './Avatar';
|
||||||
|
import { Text, ITextProps } from './Text';
|
||||||
|
import { MessageContext } from './context';
|
||||||
|
|
||||||
|
interface IMessageComposition {
|
||||||
|
Avatar: React.FC<IAvatarProps>;
|
||||||
|
Text: React.FC<ITextProps>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMyMessageProps {
|
||||||
|
isMyMessage: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Message: React.FC<IMyMessageProps> & IMessageComposition = ({ children, isMyMessage }) => {
|
||||||
|
return (
|
||||||
|
<MessageContext.Provider value={{ isMyMessage }}>
|
||||||
|
<Flex flexDir={!isMyMessage ? 'row' : 'row-reverse'} alignItems={'center'}>
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
</MessageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Message.Avatar = Avatar;
|
||||||
|
Message.Text = Text;
|
||||||
|
|
||||||
|
export { Message };
|
||||||
38
src/components/Chat/Window/Message/Text.tsx
Normal file
38
src/components/Chat/Window/Message/Text.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Text as ChakraText, useColorModeValue } from '@chakra-ui/react';
|
||||||
|
import { useMessageContext } from './context';
|
||||||
|
|
||||||
|
export interface ITextProps {
|
||||||
|
content: string;
|
||||||
|
time: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Text: React.FC<ITextProps> = ({ content, time }) => {
|
||||||
|
const { isMyMessage } = useMessageContext();
|
||||||
|
|
||||||
|
const bgColor = !isMyMessage ? useColorModeValue('gray.50', 'gray.700') : useColorModeValue('gray.100', 'gray.600');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box mx={'4'} maxW={'450px'} bg={bgColor} borderRadius='3xl' px='6' py='3'>
|
||||||
|
<ChakraText fontSize={'15px'}>
|
||||||
|
{content}
|
||||||
|
<Box
|
||||||
|
as='time'
|
||||||
|
userSelect={'none'}
|
||||||
|
position={'relative'}
|
||||||
|
left='10px'
|
||||||
|
float='right'
|
||||||
|
top='8px'
|
||||||
|
alignSelf={'flex-end'}
|
||||||
|
color='gray.500'
|
||||||
|
fontSize={'xs'}
|
||||||
|
dateTime={time}
|
||||||
|
>
|
||||||
|
{time}
|
||||||
|
</Box>
|
||||||
|
</ChakraText>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Text };
|
||||||
17
src/components/Chat/Window/Message/context.ts
Normal file
17
src/components/Chat/Window/Message/context.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
export type MessageContextType = {
|
||||||
|
isMyMessage: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MessageContext = createContext<MessageContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useMessageContext = () => {
|
||||||
|
const context = useContext(MessageContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('MessageContext was outside of its provider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
1
src/components/Chat/Window/Message/index.ts
Normal file
1
src/components/Chat/Window/Message/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { Message } from './Message';
|
||||||
64
src/components/Chat/Window/Window.tsx
Normal file
64
src/components/Chat/Window/Window.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { GridItem, GridItemProps, Flex, Box, Icon, Text, Input, Heading, IconButton, Stack } from '@chakra-ui/react';
|
||||||
|
import { MdOutlineChat, MdSend } from 'react-icons/md';
|
||||||
|
import { Message } from './Message';
|
||||||
|
|
||||||
|
const Window: React.FC<GridItemProps> = props => {
|
||||||
|
const chatId = new URLSearchParams(location.search).get('chat_id');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('chat id changed!');
|
||||||
|
}, [chatId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridItem pt={'7rem'} pb='5' px='12' {...props}>
|
||||||
|
{chatId ? (
|
||||||
|
<Flex flexDir={'column'}>
|
||||||
|
<Box my='3'>
|
||||||
|
<Heading as='h2' fontWeight={'semibold'} fontSize='3xl'>
|
||||||
|
John Doe
|
||||||
|
</Heading>
|
||||||
|
</Box>
|
||||||
|
<Stack h='65vh' overflowY={'scroll'} mb='12' mt='6' spacing={'3'} pr='8'>
|
||||||
|
<Message isMyMessage={false}>
|
||||||
|
<Message.Avatar fullname='John Doe' />
|
||||||
|
<Message.Text content={'Lorem ipsum dolor sit amet consectetur adipisicing elit.'} time='18:00' />
|
||||||
|
</Message>
|
||||||
|
<Message isMyMessage={true}>
|
||||||
|
<Message.Avatar fullname='Sergey Yarkov' />
|
||||||
|
<Message.Text content={'Lorem ipsum dolor sit amet consectetur.'} time='18:00' />
|
||||||
|
</Message>
|
||||||
|
</Stack>
|
||||||
|
<Flex mt='auto' alignItems={'flex-end'}>
|
||||||
|
<Input
|
||||||
|
resize={'block'}
|
||||||
|
type='text'
|
||||||
|
placeholder='Write a message...'
|
||||||
|
maxW='900px'
|
||||||
|
variant={'outline'}
|
||||||
|
rounded='xl'
|
||||||
|
mr='2'
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
icon={<MdSend />}
|
||||||
|
aria-label='Send message'
|
||||||
|
variant={'ghost'}
|
||||||
|
_hover={{ bg: 'inherit' }}
|
||||||
|
_active={{ bg: 'inherit' }}
|
||||||
|
_focus={{ outline: 'none' }}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
) : (
|
||||||
|
<Box textAlign={'center'} mt={{ base: '0', lg: '15rem' }}>
|
||||||
|
<Icon as={MdOutlineChat} color='gray.600' w='16' h='16' />
|
||||||
|
<Text color='gray.500' userSelect={'none'}>
|
||||||
|
Select a chat to start messaging
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</GridItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Window };
|
||||||
1
src/components/Chat/Window/index.ts
Normal file
1
src/components/Chat/Window/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { Window } from './Window';
|
||||||
21
src/components/Chat/context.ts
Normal file
21
src/components/Chat/context.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React, { useContext, createContext } from 'react';
|
||||||
|
|
||||||
|
export type ChatContextType = {
|
||||||
|
search: string;
|
||||||
|
setSearch: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChatContext = createContext<ChatContextType>({
|
||||||
|
search: '',
|
||||||
|
setSearch: () => undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useChatContext = () => {
|
||||||
|
const context = useContext(ChatContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('ChatContext was outside of its provider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@ -2,17 +2,21 @@ import React from 'react';
|
|||||||
import { Grid, GridItemProps, GridProps } from '@chakra-ui/react';
|
import { Grid, GridItemProps, GridProps } from '@chakra-ui/react';
|
||||||
import { Sidebar } from './Sidebar';
|
import { Sidebar } from './Sidebar';
|
||||||
import { Window } from './Window';
|
import { Window } from './Window';
|
||||||
|
import { ISidebarComposition } from './Sidebar/Sidebar';
|
||||||
|
import { ChatContextProvider } from './ChatContextProvider';
|
||||||
|
|
||||||
interface IChatComposition {
|
interface IChatComposition {
|
||||||
Sidebar: React.FC<GridItemProps>;
|
Sidebar: React.FC<GridItemProps> & ISidebarComposition;
|
||||||
Window: React.FC<GridItemProps>;
|
Window: React.FC<GridItemProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Chat: React.FC<GridProps> & IChatComposition = props => {
|
const Chat: React.FC<GridProps> & IChatComposition = props => {
|
||||||
return (
|
return (
|
||||||
<Grid gridTemplateColumns={'1fr 3fr'} h='100vh' {...props}>
|
<ChatContextProvider>
|
||||||
{props.children}
|
<Grid gridTemplateColumns={{ base: '1fr', lg: '1fr 3fr' }} h='100vh' {...props}>
|
||||||
</Grid>
|
{props.children}
|
||||||
|
</Grid>
|
||||||
|
</ChatContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import type { IChatButtonProps } from '.';
|
|||||||
const ChatButton: React.FC<IChatButtonProps & ButtonProps> = props => {
|
const ChatButton: React.FC<IChatButtonProps & ButtonProps> = props => {
|
||||||
const { userid } = props;
|
const { userid } = props;
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const handleChat = () => history.push(`/chat/${userid}`);
|
const handleChat = () => history.push(`/messages?chat_id=${userid}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button onClick={handleChat} mt='5' variant={'outline'} size={'sm'} leftIcon={<MdOutlineMessage />} {...props}>
|
<Button onClick={handleChat} mt='5' variant={'outline'} size={'sm'} leftIcon={<MdOutlineMessage />} {...props}>
|
||||||
|
|||||||
@ -1,28 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
AvatarBadge,
|
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
WrapItem,
|
|
||||||
Text,
|
|
||||||
Stack,
|
|
||||||
Input,
|
|
||||||
Icon,
|
|
||||||
Button,
|
|
||||||
HStack,
|
|
||||||
InputGroup,
|
|
||||||
InputLeftElement,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { Link as ReactRouterLink } from 'react-router-dom';
|
|
||||||
import { Page } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
|
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
import { MdOutlineChat } from 'react-icons/md';
|
|
||||||
import { LinkBox, LinkOverlay } from '@chakra-ui/react';
|
|
||||||
import { SearchIcon } from '@chakra-ui/icons';
|
|
||||||
import { Chat } from '@educt/components/Chat';
|
import { Chat } from '@educt/components/Chat';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,75 +12,10 @@ const MessagesPage: React.FC<IPageProps> = () => {
|
|||||||
<Page.Content>
|
<Page.Content>
|
||||||
<Chat>
|
<Chat>
|
||||||
<Chat.Sidebar>
|
<Chat.Sidebar>
|
||||||
<Flex my='4' justifyContent={'space-between'} alignItems={'center'}>
|
<Chat.Sidebar.Heading />
|
||||||
<Heading as='h2' fontSize={'md'} fontWeight='medium' mr='9'>
|
<Chat.Sidebar.Users />
|
||||||
Chats
|
|
||||||
</Heading>
|
|
||||||
<InputGroup maxW='230px' size='sm'>
|
|
||||||
<InputLeftElement pointerEvents='none'>
|
|
||||||
<SearchIcon color='gray.300' />
|
|
||||||
</InputLeftElement>
|
|
||||||
<Input type='text' placeholder='Search' borderRadius={'md'} />
|
|
||||||
</InputGroup>
|
|
||||||
</Flex>
|
|
||||||
<HStack>
|
|
||||||
<Button isActive variant={'ghost'} size='sm'>
|
|
||||||
All
|
|
||||||
</Button>
|
|
||||||
<Button colorScheme={'blue'} variant='ghost' size='sm'>
|
|
||||||
Unread
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
<Stack mt='12' maxH='690px' overflowY={'scroll'} pr='3'>
|
|
||||||
{Array(20)
|
|
||||||
.fill(0)
|
|
||||||
.map((e, i) => i)
|
|
||||||
.map(n => (
|
|
||||||
<LinkBox
|
|
||||||
key={n}
|
|
||||||
py='5'
|
|
||||||
px='4'
|
|
||||||
bg={n === 2 ? useColorModeValue('gray.50', 'gray.700') : ''}
|
|
||||||
borderRadius={'lg'}
|
|
||||||
_hover={{ bg: `${useColorModeValue('gray.50', 'gray.700')}` }}
|
|
||||||
transition='all .1s'
|
|
||||||
>
|
|
||||||
<Flex flexDir={'row'} alignItems={'center'}>
|
|
||||||
<WrapItem>
|
|
||||||
<Avatar name='Sergey Yarkov' size='md'>
|
|
||||||
<AvatarBadge boxSize='0.90em' bg='green.500' />
|
|
||||||
</Avatar>
|
|
||||||
</WrapItem>
|
|
||||||
<Box ml='4'>
|
|
||||||
<LinkOverlay as={ReactRouterLink} to='/'>
|
|
||||||
<Text fontWeight={'semibold'}>
|
|
||||||
Sergey Yarkov <br />
|
|
||||||
<Text as={'span'} fontWeight='normal' fontSize={'sm'} color='gray.500'>
|
|
||||||
Sergey: hello, how you doing?
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</LinkOverlay>
|
|
||||||
</Box>
|
|
||||||
<Box alignSelf={'flex-end'} ml='auto'>
|
|
||||||
<Text as='span' color='gray.500'>
|
|
||||||
2h
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</LinkBox>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Chat.Sidebar>
|
</Chat.Sidebar>
|
||||||
<Chat.Window>
|
<Chat.Window />
|
||||||
<Flex justifyContent={'center'} mt='20rem'>
|
|
||||||
<Box textAlign={'center'}>
|
|
||||||
<Icon as={MdOutlineChat} color='gray.600' w='16' h='16' />
|
|
||||||
<Text color='gray.500' userSelect={'none'}>
|
|
||||||
Select a chat to start messaging
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Chat.Window>
|
|
||||||
</Chat>
|
</Chat>
|
||||||
</Page.Content>
|
</Page.Content>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user