Files
casdoor/web/src/ChatBox.js

200 lines
6.3 KiB
JavaScript
Raw Normal View History

2023-04-09 22:42:33 +08:00
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
2023-04-23 00:25:09 +08:00
import {Avatar, Input, List, Spin} from "antd";
2023-04-10 00:30:13 +08:00
import {CopyOutlined, DislikeOutlined, LikeOutlined, SendOutlined} from "@ant-design/icons";
2023-04-23 00:25:09 +08:00
import i18next from "i18next";
2023-04-10 00:30:13 +08:00
const {TextArea} = Input;
2023-04-09 22:42:33 +08:00
class ChatBox extends React.Component {
2023-04-10 00:30:13 +08:00
constructor(props) {
super(props);
this.state = {
inputValue: "",
};
2023-04-17 20:40:35 +08:00
this.listContainerRef = React.createRef();
}
componentDidUpdate(prevProps) {
2023-04-23 00:25:09 +08:00
if (prevProps.messages !== this.props.messages && this.props.messages !== null) {
2023-04-17 20:40:35 +08:00
this.scrollToListItem(this.props.messages.length);
}
2023-04-10 00:30:13 +08:00
}
handleKeyDown = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
if (this.state.inputValue !== "") {
this.send(this.state.inputValue);
this.setState({inputValue: ""});
}
}
};
2023-04-17 20:40:35 +08:00
scrollToListItem(index) {
const listContainerElement = this.listContainerRef.current;
if (!listContainerElement) {
return;
}
const targetItem = listContainerElement.querySelector(
`#chatbox-list-item-${index}`
);
if (!targetItem) {
return;
}
const scrollDistance = targetItem.offsetTop - listContainerElement.offsetTop;
listContainerElement.scrollTo({
top: scrollDistance,
behavior: "smooth",
});
}
2023-04-10 00:30:13 +08:00
send = (text) => {
2023-04-17 20:40:35 +08:00
this.props.sendMessage(text);
2023-04-10 00:30:13 +08:00
this.setState({inputValue: ""});
};
2023-04-10 00:55:43 +08:00
renderList() {
2023-04-23 00:25:09 +08:00
if (this.props.messages === undefined || this.props.messages === null) {
return (
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
<Spin size="large" tip={i18next.t("login:Loading")} style={{paddingTop: "20%"}} />
</div>
);
}
2023-04-09 22:42:33 +08:00
return (
2023-04-17 20:40:35 +08:00
<div ref={this.listContainerRef} style={{position: "relative", maxHeight: "calc(100vh - 140px)", overflowY: "auto"}}>
2023-04-10 00:30:13 +08:00
<List
itemLayout="horizontal"
2023-04-23 00:25:09 +08:00
dataSource={[...this.props.messages, {}]}
renderItem={(item, index) => {
if (Object.keys(item).length === 0 && item.constructor === Object) {
2023-04-17 20:40:35 +08:00
return <List.Item id={`chatbox-list-item-${index}`} style={{
height: "160px",
backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)",
borderBottom: "1px solid rgb(229, 229, 229)",
position: "relative",
}} />;
}
return (
2023-04-17 20:40:35 +08:00
<List.Item id={`chatbox-list-item-${index}`} style={{
backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)",
borderBottom: "1px solid rgb(229, 229, 229)",
position: "relative",
}}>
<div style={{width: "800px", margin: "0 auto", position: "relative"}}>
<List.Item.Meta
avatar={<Avatar style={{width: "30px", height: "30px", borderRadius: "3px"}} src={item.author === `${this.props.account.owner}/${this.props.account.name}` ? this.props.account.avatar : "https://cdn.casbin.com/casdoor/resource/built-in/admin/gpt.png"} />}
title={<div style={{fontSize: "16px", fontWeight: "normal", lineHeight: "24px", marginTop: "-15px", marginLeft: "5px", marginRight: "80px"}}>{item.text}</div>}
/>
<div style={{position: "absolute", top: "0px", right: "0px"}}
>
<CopyOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
<LikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
<DislikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
</div>
2023-04-10 00:55:43 +08:00
</div>
</List.Item>
);
}}
2023-04-10 00:30:13 +08:00
/>
2023-04-10 00:55:43 +08:00
<div style={{
position: "absolute",
bottom: 0,
left: 0,
right: 0,
height: "120px",
background: "linear-gradient(transparent 0%, rgba(255, 255, 255, 0.8) 50%, white 100%)",
pointerEvents: "none",
}} />
</div>
);
}
renderInput() {
return (
<div
style={{
position: "fixed",
bottom: "90px",
width: "100%",
display: "flex",
justifyContent: "center",
}}
>
<div style={{position: "relative", width: "760px", marginLeft: "-280px"}}>
<TextArea
placeholder={"Send a message..."}
autoSize={{maxRows: 8}}
value={this.state.inputValue}
onChange={(e) => this.setState({inputValue: e.target.value})}
onKeyDown={this.handleKeyDown}
style={{
fontSize: "16px",
fontWeight: "normal",
lineHeight: "24px",
width: "770px",
height: "48px",
borderRadius: "6px",
borderColor: "rgb(229,229,229)",
boxShadow: "0 0 15px rgba(0, 0, 0, 0.1)",
paddingLeft: "17px",
paddingRight: "17px",
paddingTop: "12px",
paddingBottom: "12px",
}}
suffix={<SendOutlined style={{color: "rgb(210,210,217"}} onClick={() => this.send(this.state.inputValue)} />}
autoComplete="off"
/>
<SendOutlined
style={{
color: this.state.inputValue === "" ? "rgb(210,210,217)" : "rgb(142,142,160)",
position: "absolute",
bottom: "17px",
right: "17px",
}}
onClick={() => this.send(this.state.inputValue)}
/>
2023-04-10 00:30:13 +08:00
</div>
</div>
2023-04-09 22:42:33 +08:00
);
}
2023-04-10 00:55:43 +08:00
render() {
return (
<div>
{
this.renderList()
}
{
this.renderInput()
}
</div>
);
}
2023-04-09 22:42:33 +08:00
}
export default ChatBox;