style: optimize layout

main
Justin Xiao 2023-07-21 18:10:42 +08:00
parent d90e19284a
commit 419af7a9d5
3 changed files with 92 additions and 71 deletions

View File

@ -124,56 +124,76 @@ export default function Home() {
return ( return (
<> <>
<Container size="md" h={"100vh"}> <Container size="md" h={"100vh"}>
<Card shadow="sm" padding="sm" radius="md" withBorder h={"100%"}> <Card
<Card.Section withBorder inheritPadding py="xs" h={"10%"}> 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 <ChatroomTitle
targetSocketId={targetSocketId} targetSocketId={targetSocketId}
setTargetSocketId={setTargetSocketId} setTargetSocketId={setTargetSocketId}
/> />
</Card.Section> </Card.Section>
<ScrollArea offsetScrollbars viewportRef={chatViewportRef} h={"85%"}> <Card.Section withBorder inheritPadding py="xs" h={"85%"} mih={"300px"}>
{messages.map((message, index) => { <ScrollArea offsetScrollbars viewportRef={chatViewportRef} h={"100%"}>
return ( {messages.map((message, index) => {
<div return (
className={ <div
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
className={ className={
message.me ? classes.rightMessage : classes.leftMessage message.me
? classes.rightMessageField
: classes.leftMessageField
} }
key={message.timestamp + index}
> >
{message.message.split("\n").map((line, index) => { {!message.me && (
return ( <div className={classes.avatar}>
<span key={message.timestamp + index}> <Avatar alt="User" color="blue" radius="xl">
{line} {onlineUsers[message.from] &&
<br /> onlineUsers[message.from].length > 5
</span> ? `${onlineUsers[message.from].slice(0, 1)}`
); : onlineUsers[message.from]}
})} </Avatar>
</Text> </div>
<Text size="xs" className={classes.timestamp}> )}
{new Date(message.timestamp).toLocaleTimeString()} <Text
</Text> className={
</div> message.me
); ? classes.rightMessage
})} : classes.leftMessage
</ScrollArea> }
<Card.Section withBorder inheritPadding h={"10%"}> >
{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} /> <ChatroomInput targetSocketId={targetSocketId} />
</Card.Section> </Card.Section>
</Card> </Card>

View File

@ -8,7 +8,7 @@ type Props = {
targetSocketId: string; targetSocketId: string;
}; };
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
inputWithoutBorder: { Textarea: {
border: "none", border: "none",
}, },
})); }));
@ -58,25 +58,21 @@ const ChatroomInput: FC<Props> = ({ targetSocketId }) => {
}; };
return ( return (
<Group w={"100%"} align="center"> <Group w={"100%"} display={"flex"} className="gap-0" position="apart" noWrap>
<Textarea <Textarea
classNames={{ input: classes.inputWithoutBorder }}
h={"100%"}
w={"100%"} w={"100%"}
classNames={{ input: classes.Textarea }}
minRows={3}
maxRows={7}
ref={messageInputRef} ref={messageInputRef}
value={message} value={message}
onChange={(e) => setMessage(e.currentTarget.value)} 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..." placeholder="Type something..."
rightSectionWidth={42}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
/> />
<ActionIcon radius="xl">
<IconSend stroke={1.5} onClick={sendMessage} />
</ActionIcon>
</Group> </Group>
); );
}; };

View File

@ -39,9 +39,9 @@ type Props = {
const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => { const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
const { socket, emitMode, setEmitMode, connect, disconnect } = useSocketStore(); // deconstructing socket and its method from socket store 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 [onlineUsers, setOnlineUsers] = useState<Record<string, string>>({}); // online users
const [opened, setOpened] = useState(false); const [socketIdPopoverOpen, setSocketIdPopoverOpen] = useState(false);
useEffect(() => { useEffect(() => {
if (!socket) return; if (!socket) return;
@ -56,22 +56,21 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
return ( 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> <Group noWrap>
<Avatar /> <Avatar />
<Popover <Popover
width="fit-content" width="fit-content"
position="bottom" position="bottom"
withArrow withArrow
shadow="md" shadow="md"
opened={popoverOpened} opened={avatarPopoverOpen}
onChange={setPopoverOpened} onChange={setAvatarPopoverOpen}
> >
<Popover.Target> <Popover.Target>
<ActionIcon <ActionIcon
variant="subtle" variant="subtle"
onClick={() => setPopoverOpened((open) => !open)} onClick={() => setAvatarPopoverOpen((open) => !open)}
> >
<IconChevronDown size="1rem" /> <IconChevronDown size="1rem" />
</ActionIcon> </ActionIcon>
@ -135,20 +134,25 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
crossAxis: 30, crossAxis: 30,
}} }}
shadow="md" shadow="md"
opened={opened} opened={socketIdPopoverOpen}
onChange={setOpened} onChange={setSocketIdPopoverOpen}
> >
<Popover.Target> <Popover.Target>
<SegmentedControl <SegmentedControl
size="xs" size="xs"
value={emitMode} value={emitMode}
onClick={() => {
if (emitMode === "private_message") {
setSocketIdPopoverOpen(true);
}
}}
onChange={(value: "broadcast" | "private_message") => { onChange={(value: "broadcast" | "private_message") => {
setEmitMode(value); setEmitMode(value);
if (value === "broadcast") { if (value === "broadcast") {
setTargetSocketId(""); setTargetSocketId("");
setOpened(false); setSocketIdPopoverOpen(false);
} else { } else {
setOpened(true); setSocketIdPopoverOpen(true);
} }
}} }}
data={[ data={[
@ -165,10 +169,7 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
value: "private_message", value: "private_message",
label: ( label: (
<Center> <Center>
<IconUserShare <IconUserShare size="1rem" />
size="1rem"
onClick={() => setOpened(true)}
/>
<Box ml={10}>To</Box> <Box ml={10}>To</Box>
</Center> </Center>
), ),
@ -205,7 +206,7 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
/> />
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover>
<Menu shadow="md" width="fit-content"> <Menu shadow="md">
<Menu.Target> <Menu.Target>
<ActionIcon variant="subtle"> <ActionIcon variant="subtle">
<IconUserCog size="1.25em" /> <IconUserCog size="1.25em" />
@ -224,7 +225,11 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
.map((socketId) => ( .map((socketId) => (
<Menu.Item <Menu.Item
key={socketId} key={socketId}
onClick={() => setTargetSocketId(socketId)} onClick={() => {
setEmitMode("private_message");
setSocketIdPopoverOpen(true);
setTargetSocketId(socketId);
}}
> >
<Group <Group
position="apart" position="apart"