Commit 02af704a authored by Chunchi Che's avatar Chunchi Che

添加决斗界面的聊天框

parent bc9d35df
...@@ -19,6 +19,7 @@ import { ...@@ -19,6 +19,7 @@ import {
YesNoModal, YesNoModal,
} from "./Message"; } from "./Message";
import { LifeBar, Mat, Menu, Underlying } from "./PlayMat"; import { LifeBar, Mat, Menu, Underlying } from "./PlayMat";
import { ChatBox } from "./PlayMat/ChatBox";
export const Component: React.FC = () => { export const Component: React.FC = () => {
const { stage } = useSnapshot(sideStore); const { stage } = useSnapshot(sideStore);
...@@ -57,6 +58,7 @@ export const Component: React.FC = () => { ...@@ -57,6 +58,7 @@ export const Component: React.FC = () => {
<SortCardModal /> <SortCardModal />
<SimpleSelectCardsModal /> <SimpleSelectCardsModal />
<EndModal /> <EndModal />
<ChatBox />
</> </>
); );
}; };
......
.chatbox {
position: relative;
height: 60% !important;
width: 60% !important;
background-color: #0d0d0d8f !important;
left: 20%;
bottom: -30%;
border-radius: 6px;
:global(.ant-drawer-header) {
padding: 8px 4px;
}
:global(.ant-drawer-body) {
padding: 16px;
}
}
.container {
display: flex;
flex-direction: column;
height: 100%;
max-height: 100%;
}
.input {
display: flex;
width: 100%;
margin-top: auto;
background-color: hsla(0, 0%, 100%, 0.07);
padding: 4px;
border-radius: 4px;
}
.dialogs {
display: flex;
flex-direction: column;
position: absolute;
padding: 8px 0;
width: 100%;
gap: 2px;
.item {
vertical-align: baseline;
.name, .content {
display: inline-block;
}
.name {
font-weight: 500;
line-height: 1.375rem;
}
.content {
font-size: 14px;
color: green;
margin-top: 2px;
}
}
}
import { DownOutlined } from "@ant-design/icons";
import { Button, Drawer, Input } from "antd";
import React from "react";
import { proxy, useSnapshot } from "valtio";
import { IconFont, ScrollableArea, useChat } from "@/ui/Shared";
import styles from "./index.module.scss";
const store = proxy({ open: false });
interface ChatItem {
name: string;
content: string;
}
export const ChatBox: React.FC = () => {
const { open } = useSnapshot(store);
const { dialogs, input, setInput, ref, onSend } = useChat();
const onClose = () => (store.open = false);
return (
<Drawer
open={open}
placement="bottom"
mask={false}
className={styles.chatbox}
onClose={onClose}
maskClosable
closeIcon={<DownOutlined />}
>
<div className={styles.container}>
<ScrollableArea className={styles.dialogs} ref={ref}>
{dialogs.map((item, idx) => (
<DialogItem key={idx} {...item} />
))}
</ScrollableArea>
<div className={styles.input}>
<Input.TextArea
bordered={false}
value={input}
onChange={(event) => setInput(event.target.value)}
autoSize
placeholder="请输入聊天内容"
onPressEnter={(e) => {
e.preventDefault();
onSend();
}}
/>
<Button
type="text"
icon={<IconFont type="icon-send" size={14} />}
onClick={onSend}
/>
</div>
</div>
</Drawer>
);
};
const DialogItem: React.FC<ChatItem> = ({ name, content }) => (
<div className={styles.item}>
<div className={styles.name}>{name}</div>
<span>{` > `}</span>
<div className={styles.content}>{content}</div>
</div>
);
export const openChatBox = () => (store.open = !store.open);
...@@ -29,6 +29,7 @@ import { IconFont } from "@/ui/Shared"; ...@@ -29,6 +29,7 @@ import { IconFont } from "@/ui/Shared";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType; import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType;
import { openChatBox } from "../ChatBox";
const { phase: store } = matStore; const { phase: store } = matStore;
const { useToken } = theme; const { useToken } = theme;
...@@ -170,7 +171,11 @@ export const Menu = () => { ...@@ -170,7 +171,11 @@ export const Menu = () => {
></Button> ></Button>
</DropdownWithTitle> </DropdownWithTitle>
<Tooltip title="聊天室"> <Tooltip title="聊天室">
<Button icon={<MessageFilled />} type="text"></Button> <Button
icon={<MessageFilled />}
onClick={openChatBox}
type="text"
></Button>
</Tooltip> </Tooltip>
<DropdownWithTitle <DropdownWithTitle
title="是否投降?" title="是否投降?"
......
// 聊天框的通用Hook
import { OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
import { useEffect, useRef, useState } from "react";
import { useSnapshot } from "valtio";
import { sendChat } from "@/api";
import { chatStore, roomStore } from "@/stores";
interface ChatItem {
name: string;
time: string;
content: string;
}
export const useChat = () => {
const [chatList, setChatList] = useState<ChatItem[]>([]);
const [input, setInput] = useState<string | undefined>(undefined);
const chat = useSnapshot(chatStore);
/** 滚动条的ref */
const ref = useRef<OverlayScrollbarsComponentRef<"div">>(null);
/** dialogs 滚动到最底下 */
const scrollToBottom = () => {
const viewport = ref.current?.osInstance()?.elements().viewport;
if (viewport) {
viewport.scrollTop = viewport.scrollHeight;
}
};
/** 发信息 */
const onSend = () => {
if (input !== undefined) {
sendChat(input);
setInput("");
}
};
useEffect(() => {
if (chatStore.sender >= 0 && chatStore.message.length !== 0) {
const { sender } = chatStore;
const name =
sender < roomStore.players.length
? roomStore.players[sender]?.name ?? "?"
: (sender > 8 && sender < 11) || sender > 19
? "?"
: "System";
setChatList((prev) => [
...prev,
{
name: name,
time: formatTimeToHHMMSS(),
content: chatStore.message,
},
]);
scrollToBottom();
}
}, [chat]);
return {
dialogs: chatList,
input,
setInput,
ref,
onSend,
};
};
function formatTimeToHHMMSS() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
const seconds = String(now.getSeconds()).padStart(2, "0");
return `${hours}:${minutes}:${seconds}`;
}
export * from "./Background"; export * from "./Background";
export * from "./CardEffectText"; export * from "./CardEffectText";
export * from "./chatHook";
export * from "./css"; export * from "./css";
export * from "./DeckCard"; export * from "./DeckCard";
export * from "./DeckZone"; export * from "./DeckZone";
......
import { Button, Input } from "antd"; import { Button, Input } from "antd";
import { type OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
import { useEffect, useRef, useState } from "react";
import { useSnapshot } from "valtio";
import { sendChat } from "@/api"; import { IconFont, ScrollableArea, useChat } from "@/ui/Shared";
import { chatStore, roomStore } from "@/stores";
import { IconFont, ScrollableArea } from "@/ui/Shared";
import styles from "./Chat.module.scss"; import styles from "./Chat.module.scss";
...@@ -16,54 +11,12 @@ interface ChatItem { ...@@ -16,54 +11,12 @@ interface ChatItem {
} }
export const Chat: React.FC = () => { export const Chat: React.FC = () => {
const [chatlist, setChatlist] = useState<ChatItem[]>([]); const { dialogs, input, setInput, ref, onSend } = useChat();
const [input, setInput] = useState<string | undefined>(undefined);
const chat = useSnapshot(chatStore);
useEffect(() => {
if (chatStore.sender >= 0 && chatStore.message.length !== 0) {
const { sender } = chatStore;
const name =
sender < roomStore.players.length
? roomStore.players[sender]?.name ?? "?"
: (sender > 8 && sender < 11) || sender > 19
? "?"
: "System";
setChatlist((prev) => [
...prev,
{
name: name,
time: formatTimeToHHMMSS(),
content: chatStore.message,
},
]);
scrollToBottom();
}
}, [chat]);
/** dialogs 滚动到最底下 */
const scrollToBottom = () => {
const viewport = ref.current?.osInstance()?.elements().viewport;
if (viewport) {
viewport.scrollTop = viewport.scrollHeight;
}
};
/** 发信息 */
const send = () => {
if (input !== undefined) {
sendChat(input);
setInput("");
}
};
/** 滚动条的ref */
const ref = useRef<OverlayScrollbarsComponentRef<"div">>(null);
return ( return (
<div className={styles.chat}> <div className={styles.chat}>
<ScrollableArea className={styles.dialogs} ref={ref}> <ScrollableArea className={styles.dialogs} ref={ref}>
{chatlist.map((item, idx) => ( {dialogs.map((item, idx) => (
<DialogItem key={idx} {...item} /> <DialogItem key={idx} {...item} />
))} ))}
</ScrollableArea> </ScrollableArea>
...@@ -76,13 +29,13 @@ export const Chat: React.FC = () => { ...@@ -76,13 +29,13 @@ export const Chat: React.FC = () => {
placeholder="请输入聊天内容" placeholder="请输入聊天内容"
onPressEnter={(e) => { onPressEnter={(e) => {
e.preventDefault(); e.preventDefault();
send(); onSend();
}} }}
/> />
<Button <Button
type="text" type="text"
icon={<IconFont type="icon-send" size={16} />} icon={<IconFont type="icon-send" size={16} />}
onClick={send} onClick={onSend}
/> />
</div> </div>
</div> </div>
...@@ -100,11 +53,3 @@ const DialogItem: React.FC<ChatItem> = ({ name, time, content }) => { ...@@ -100,11 +53,3 @@ const DialogItem: React.FC<ChatItem> = ({ name, time, content }) => {
</div> </div>
); );
}; };
function formatTimeToHHMMSS() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
const seconds = String(now.getSeconds()).padStart(2, "0");
return `${hours}:${minutes}:${seconds}`;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment