style: optimize layout
parent
d90e19284a
commit
419af7a9d5
|
@ -124,56 +124,76 @@ export default function Home() {
|
|||
return (
|
||||
<>
|
||||
<Container size="md" h={"100vh"}>
|
||||
<Card shadow="sm" padding="sm" radius="md" withBorder h={"100%"}>
|
||||
<Card.Section withBorder inheritPadding py="xs" h={"10%"}>
|
||||
<Card
|
||||
shadow="sm"
|
||||
padding="sm"
|
||||
radius="md"
|
||||
withBorder
|
||||
h={"100%"}
|
||||
className="flex flex-col"
|
||||
>
|
||||
<Card.Section
|
||||
component="a"
|
||||
withBorder
|
||||
inheritPadding
|
||||
py="xs"
|
||||
h={"10%"}
|
||||
display={"flex"}
|
||||
mih={"65px"}
|
||||
>
|
||||
<ChatroomTitle
|
||||
targetSocketId={targetSocketId}
|
||||
setTargetSocketId={setTargetSocketId}
|
||||
/>
|
||||
</Card.Section>
|
||||
<ScrollArea offsetScrollbars viewportRef={chatViewportRef} h={"85%"}>
|
||||
{messages.map((message, index) => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
message.me
|
||||
? classes.rightMessageField
|
||||
: classes.leftMessageField
|
||||
}
|
||||
key={message.timestamp + index}
|
||||
>
|
||||
{!message.me && (
|
||||
<div className={classes.avatar}>
|
||||
<Avatar alt="User" color="blue" radius="xl">
|
||||
{onlineUsers[message.from] &&
|
||||
onlineUsers[message.from].length > 5
|
||||
? `${onlineUsers[message.from].slice(0, 1)}`
|
||||
: onlineUsers[message.from]}
|
||||
</Avatar>
|
||||
</div>
|
||||
)}
|
||||
<Text
|
||||
<Card.Section withBorder inheritPadding py="xs" h={"85%"} mih={"300px"}>
|
||||
<ScrollArea offsetScrollbars viewportRef={chatViewportRef} h={"100%"}>
|
||||
{messages.map((message, index) => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
message.me ? classes.rightMessage : classes.leftMessage
|
||||
message.me
|
||||
? classes.rightMessageField
|
||||
: classes.leftMessageField
|
||||
}
|
||||
key={message.timestamp + index}
|
||||
>
|
||||
{message.message.split("\n").map((line, index) => {
|
||||
return (
|
||||
<span key={message.timestamp + index}>
|
||||
{line}
|
||||
<br />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</Text>
|
||||
<Text size="xs" className={classes.timestamp}>
|
||||
{new Date(message.timestamp).toLocaleTimeString()}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ScrollArea>
|
||||
<Card.Section withBorder inheritPadding h={"10%"}>
|
||||
{!message.me && (
|
||||
<div className={classes.avatar}>
|
||||
<Avatar alt="User" color="blue" radius="xl">
|
||||
{onlineUsers[message.from] &&
|
||||
onlineUsers[message.from].length > 5
|
||||
? `${onlineUsers[message.from].slice(0, 1)}`
|
||||
: onlineUsers[message.from]}
|
||||
</Avatar>
|
||||
</div>
|
||||
)}
|
||||
<Text
|
||||
className={
|
||||
message.me
|
||||
? classes.rightMessage
|
||||
: classes.leftMessage
|
||||
}
|
||||
>
|
||||
{message.message.split("\n").map((line, index) => {
|
||||
return (
|
||||
<span key={message.timestamp + index}>
|
||||
{line}
|
||||
<br />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</Text>
|
||||
<Text size="xs" className={classes.timestamp}>
|
||||
{new Date(message.timestamp).toLocaleTimeString()}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ScrollArea>
|
||||
</Card.Section>
|
||||
|
||||
<Card.Section withBorder h={"5%"} mih={"80px"}>
|
||||
<ChatroomInput targetSocketId={targetSocketId} />
|
||||
</Card.Section>
|
||||
</Card>
|
||||
|
|
|
@ -8,7 +8,7 @@ type Props = {
|
|||
targetSocketId: string;
|
||||
};
|
||||
const useStyles = createStyles((theme) => ({
|
||||
inputWithoutBorder: {
|
||||
Textarea: {
|
||||
border: "none",
|
||||
},
|
||||
}));
|
||||
|
@ -58,25 +58,21 @@ const ChatroomInput: FC<Props> = ({ targetSocketId }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Group w={"100%"} align="center">
|
||||
<Group w={"100%"} display={"flex"} className="gap-0" position="apart" noWrap>
|
||||
<Textarea
|
||||
classNames={{ input: classes.inputWithoutBorder }}
|
||||
h={"100%"}
|
||||
w={"100%"}
|
||||
classNames={{ input: classes.Textarea }}
|
||||
minRows={3}
|
||||
maxRows={7}
|
||||
ref={messageInputRef}
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.currentTarget.value)}
|
||||
// radius="xl"
|
||||
// size="md"
|
||||
rightSection={
|
||||
<ActionIcon size={32} radius="xl">
|
||||
<IconSend size="1.5rem" stroke={1.5} onClick={sendMessage} />
|
||||
</ActionIcon>
|
||||
}
|
||||
placeholder="Type something..."
|
||||
rightSectionWidth={42}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<ActionIcon radius="xl">
|
||||
<IconSend stroke={1.5} onClick={sendMessage} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -39,9 +39,9 @@ type Props = {
|
|||
const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
||||
const { socket, emitMode, setEmitMode, connect, disconnect } = useSocketStore(); // deconstructing socket and its method from socket store
|
||||
|
||||
const [popoverOpened, setPopoverOpened] = useState(false); // control popover open/close
|
||||
const [avatarPopoverOpen, setAvatarPopoverOpen] = useState(false); // control popover open/close
|
||||
const [onlineUsers, setOnlineUsers] = useState<Record<string, string>>({}); // online users
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [socketIdPopoverOpen, setSocketIdPopoverOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket) return;
|
||||
|
@ -56,22 +56,21 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Group position="apart" mt="xs" mb="xs" noWrap h={"5vh"}>
|
||||
<Group position="apart" mt="xs" mb="xs" noWrap align="center" w={"100%"}>
|
||||
<Group noWrap>
|
||||
<Avatar />
|
||||
|
||||
<Popover
|
||||
width="fit-content"
|
||||
position="bottom"
|
||||
withArrow
|
||||
shadow="md"
|
||||
opened={popoverOpened}
|
||||
onChange={setPopoverOpened}
|
||||
opened={avatarPopoverOpen}
|
||||
onChange={setAvatarPopoverOpen}
|
||||
>
|
||||
<Popover.Target>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
onClick={() => setPopoverOpened((open) => !open)}
|
||||
onClick={() => setAvatarPopoverOpen((open) => !open)}
|
||||
>
|
||||
<IconChevronDown size="1rem" />
|
||||
</ActionIcon>
|
||||
|
@ -135,20 +134,25 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
|||
crossAxis: 30,
|
||||
}}
|
||||
shadow="md"
|
||||
opened={opened}
|
||||
onChange={setOpened}
|
||||
opened={socketIdPopoverOpen}
|
||||
onChange={setSocketIdPopoverOpen}
|
||||
>
|
||||
<Popover.Target>
|
||||
<SegmentedControl
|
||||
size="xs"
|
||||
value={emitMode}
|
||||
onClick={() => {
|
||||
if (emitMode === "private_message") {
|
||||
setSocketIdPopoverOpen(true);
|
||||
}
|
||||
}}
|
||||
onChange={(value: "broadcast" | "private_message") => {
|
||||
setEmitMode(value);
|
||||
if (value === "broadcast") {
|
||||
setTargetSocketId("");
|
||||
setOpened(false);
|
||||
setSocketIdPopoverOpen(false);
|
||||
} else {
|
||||
setOpened(true);
|
||||
setSocketIdPopoverOpen(true);
|
||||
}
|
||||
}}
|
||||
data={[
|
||||
|
@ -165,10 +169,7 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
|||
value: "private_message",
|
||||
label: (
|
||||
<Center>
|
||||
<IconUserShare
|
||||
size="1rem"
|
||||
onClick={() => setOpened(true)}
|
||||
/>
|
||||
<IconUserShare size="1rem" />
|
||||
<Box ml={10}>To</Box>
|
||||
</Center>
|
||||
),
|
||||
|
@ -205,7 +206,7 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
|||
/>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
<Menu shadow="md" width="fit-content">
|
||||
<Menu shadow="md">
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="subtle">
|
||||
<IconUserCog size="1.25em" />
|
||||
|
@ -224,7 +225,11 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
|||
.map((socketId) => (
|
||||
<Menu.Item
|
||||
key={socketId}
|
||||
onClick={() => setTargetSocketId(socketId)}
|
||||
onClick={() => {
|
||||
setEmitMode("private_message");
|
||||
setSocketIdPopoverOpen(true);
|
||||
setTargetSocketId(socketId);
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
position="apart"
|
||||
|
|
Loading…
Reference in New Issue