Improve chat UI

This commit is contained in:
Yang Luo
2023-04-22 23:18:18 +08:00
parent 916a55b633
commit d25508fa56
3 changed files with 111 additions and 25 deletions

View File

@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Menu} from "antd"; import {Button, Menu} from "antd";
import {LayoutOutlined} from "@ant-design/icons"; import {DeleteOutlined, LayoutOutlined, PlusOutlined} from "@ant-design/icons";
class ChatMenu extends React.Component { class ChatMenu extends React.Component {
constructor(props) { constructor(props) {
@ -38,6 +38,7 @@ class ChatMenu extends React.Component {
categories[chat.category].push(chat); categories[chat.category].push(chat);
}); });
const selectedKeys = this.state === undefined ? [] : this.state.selectedKeys;
return Object.keys(categories).map((category, index) => { return Object.keys(categories).map((category, index) => {
return { return {
key: `${index}`, key: `${index}`,
@ -45,10 +46,50 @@ class ChatMenu extends React.Component {
label: category, label: category,
children: categories[category].map((chat, chatIndex) => { children: categories[category].map((chat, chatIndex) => {
const globalChatIndex = chats.indexOf(chat); const globalChatIndex = chats.indexOf(chat);
const isSelected = selectedKeys.includes(`${index}-${chatIndex}`);
return { return {
key: `${index}-${chatIndex}`, key: `${index}-${chatIndex}`,
index: globalChatIndex, index: globalChatIndex,
label: chat.displayName, label: (
<div
className="menu-item-container"
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
{chat.displayName}
{isSelected && (
<DeleteOutlined
className="menu-item-delete-icon"
style={{
visibility: "visible",
color: "inherit",
transition: "color 0.3s",
}}
onMouseEnter={(e) => {
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.color = "inherit";
}}
onMouseDown={(e) => {
e.currentTarget.style.color = "rgba(89,54,213,0.4)";
}}
onMouseUp={(e) => {
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
}}
onClick={(e) => {
e.stopPropagation();
if (this.props.onDelete) {
this.props.onDelete(globalChatIndex);
}
}}
/>
)}
</div>
),
}; };
}), }),
}; };
@ -85,14 +126,40 @@ class ChatMenu extends React.Component {
const items = this.chatsToItems(this.props.chats); const items = this.chatsToItems(this.props.chats);
return ( return (
<Menu <>
mode="inline" <Button
openKeys={this.state.openKeys} icon={<PlusOutlined />}
selectedKeys={this.state.selectedKeys} style={{
onOpenChange={this.onOpenChange} width: "calc(100% - 8px)",
onSelect={this.onSelect} height: "40px",
items={items} margin: "4px",
/> borderColor: "rgb(229,229,229)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "rgba(89,54,213,0.6)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "rgba(0, 0, 0, 0.1)";
}}
onMouseDown={(e) => {
e.currentTarget.style.borderColor = "rgba(89,54,213,0.4)";
}}
onMouseUp={(e) => {
e.currentTarget.style.borderColor = "rgba(89,54,213,0.6)";
}}
onClick={this.props.onNewChat}
>
New Chat
</Button>
<Menu
mode="inline"
openKeys={this.state.openKeys}
selectedKeys={this.state.selectedKeys}
onOpenChange={this.onOpenChange}
onSelect={this.onSelect}
items={items}
/>
</>
); );
} }
} }

View File

@ -125,6 +125,8 @@ class ProviderEditPage extends React.Component {
} else { } else {
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip")); return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
} }
case "AI":
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
default: default:
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip")); return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
} }
@ -278,17 +280,20 @@ class ProviderEditPage extends React.Component {
this.updateProviderField("type", "Alipay"); this.updateProviderField("type", "Alipay");
} else if (value === "Captcha") { } else if (value === "Captcha") {
this.updateProviderField("type", "Default"); this.updateProviderField("type", "Default");
} else if (value === "AI") {
this.updateProviderField("type", "OpenAI API - GPT");
} }
})}> })}>
{ {
[ [
{id: "OAuth", name: "OAuth"}, {id: "AI", name: "AI"},
{id: "Captcha", name: "Captcha"},
{id: "Email", name: "Email"}, {id: "Email", name: "Email"},
{id: "OAuth", name: "OAuth"},
{id: "Payment", name: "Payment"},
{id: "SAML", name: "SAML"},
{id: "SMS", name: "SMS"}, {id: "SMS", name: "SMS"},
{id: "Storage", name: "Storage"}, {id: "Storage", name: "Storage"},
{id: "SAML", name: "SAML"},
{id: "Payment", name: "Payment"},
{id: "Captcha", name: "Captcha"},
] ]
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => a.name.localeCompare(b.name))
.map((providerCategory, index) => <Option key={index} value={providerCategory.id}>{providerCategory.name}</Option>) .map((providerCategory, index) => <Option key={index} value={providerCategory.id}>{providerCategory.name}</Option>)
@ -437,16 +442,20 @@ class ProviderEditPage extends React.Component {
{ {
this.state.provider.category === "Captcha" && this.state.provider.type === "Default" ? null : ( this.state.provider.category === "Captcha" && this.state.provider.type === "Default" ? null : (
<React.Fragment> <React.Fragment>
<Row style={{marginTop: "20px"}} > {
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> this.state.provider.category === "AI" ? null : (
{this.getClientIdLabel(this.state.provider)} <Row style={{marginTop: "20px"}} >
</Col> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
<Col span={22} > {this.getClientIdLabel(this.state.provider)}
<Input value={this.state.provider.clientId} onChange={e => { </Col>
this.updateProviderField("clientId", e.target.value); <Col span={22} >
}} /> <Input value={this.state.provider.clientId} onChange={e => {
</Col> this.updateProviderField("clientId", e.target.value);
</Row> }} />
</Col>
</Row>
)
}
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientSecretLabel(this.state.provider)} {this.getClientSecretLabel(this.state.provider)}

View File

@ -204,6 +204,12 @@ export const OtherProviderInfo = {
url: "https://www.cloudflare.com/products/turnstile/", url: "https://www.cloudflare.com/products/turnstile/",
}, },
}, },
AI: {
"OpenAI API - GPT": {
logo: `${StaticBaseUrl}/img/social_openai.svg`,
url: "https://platform.openai.com",
},
},
}; };
export function initCountries() { export function initCountries() {
@ -855,6 +861,10 @@ export function getProviderTypeOptions(category) {
{id: "GEETEST", name: "GEETEST"}, {id: "GEETEST", name: "GEETEST"},
{id: "Cloudflare Turnstile", name: "Cloudflare Turnstile"}, {id: "Cloudflare Turnstile", name: "Cloudflare Turnstile"},
]); ]);
} else if (category === "AI") {
return ([
{id: "OpenAI API - GPT", name: "OpenAI API - GPT"},
]);
} else { } else {
return []; return [];
} }