Commit a61a3f2d authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/chatbox' into 'main'

添加决斗界面的聊天框

See merge request mycard/Neos!320
parents bc9d35df 02af704a
......@@ -19,6 +19,7 @@ import {
YesNoModal,
} from "./Message";
import { LifeBar, Mat, Menu, Underlying } from "./PlayMat";
import { ChatBox } from "./PlayMat/ChatBox";
export const Component: React.FC = () => {
const { stage } = useSnapshot(sideStore);
......@@ -57,6 +58,7 @@ export const Component: React.FC = () => {
<SortCardModal />
<SimpleSelectCardsModal />
<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";
import styles from "./index.module.scss";
import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType;
import { openChatBox } from "../ChatBox";
const { phase: store } = matStore;
const { useToken } = theme;
......@@ -170,7 +171,11 @@ export const Menu = () => {
></Button>
</DropdownWithTitle>
<Tooltip title="聊天室">
<Button icon={<MessageFilled />} type="text"></Button>
<Button
icon={<MessageFilled />}
onClick={openChatBox}
type="text"
></Button>
</Tooltip>
<DropdownWithTitle
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 "./CardEffectText";
export * from "./chatHook";
export * from "./css";
export * from "./DeckCard";
export * from "./DeckZone";
......
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 { chatStore, roomStore } from "@/stores";
import { IconFont, ScrollableArea } from "@/ui/Shared";
import { IconFont, ScrollableArea, useChat } from "@/ui/Shared";
import styles from "./Chat.module.scss";
......@@ -16,54 +11,12 @@ interface ChatItem {
}
export const Chat: React.FC = () => {
const [chatlist, setChatlist] = useState<ChatItem[]>([]);
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);
const { dialogs, input, setInput, ref, onSend } = useChat();
return (
<div className={styles.chat}>
<ScrollableArea className={styles.dialogs} ref={ref}>
{chatlist.map((item, idx) => (
{dialogs.map((item, idx) => (
<DialogItem key={idx} {...item} />
))}
</ScrollableArea>
......@@ -76,13 +29,13 @@ export const Chat: React.FC = () => {
placeholder="请输入聊天内容"
onPressEnter={(e) => {
e.preventDefault();
send();
onSend();
}}
/>
<Button
type="text"
icon={<IconFont type="icon-send" size={16} />}
onClick={send}
onClick={onSend}
/>
</div>
</div>
......@@ -100,11 +53,3 @@ const DialogItem: React.FC<ChatItem> = ({ name, time, content }) => {
</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