Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
leedom92 committed Apr 6, 2023
2 parents b8a605f + 4d675c1 commit a8a8bec
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 36 deletions.
2 changes: 1 addition & 1 deletion app/components/button.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
user-select: none;
outline: none;
border: none;
color: rgb(51, 51, 51);
color: var(--black);

&[disabled] {
cursor: not-allowed;
Expand Down
6 changes: 2 additions & 4 deletions app/components/chat-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export function ChatList() {
state.removeSession,
state.moveSession,
]);
const chatStore = useChatStore();

const onDragEnd: OnDragEndResponder = (result) => {
const { destination, source } = result;
Expand Down Expand Up @@ -95,10 +96,7 @@ export function ChatList() {
index={i}
selected={i === selectedIndex}
onClick={() => selectSession(i)}
onDelete={() =>
(!isMobileScreen() || confirm(Locale.Home.DeleteChat)) &&
removeSession(i)
}
onDelete={chatStore.deleteSession}
/>
))}
{provided.placeholder}
Expand Down
16 changes: 6 additions & 10 deletions app/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { memo, useState, useRef, useEffect, useLayoutEffect } from "react";
import SendWhiteIcon from "../icons/send-white.svg";
import BrainIcon from "../icons/brain.svg";
import ExportIcon from "../icons/export.svg";
import MenuIcon from "../icons/menu.svg";
import ReturnIcon from "../icons/return.svg";
import CopyIcon from "../icons/copy.svg";
import DownloadIcon from "../icons/download.svg";
import LoadingIcon from "../icons/three-dots.svg";
Expand Down Expand Up @@ -404,6 +404,7 @@ export function Chat(props: {

// submit user input
const onUserSubmit = () => {
if (userInput.length <= 0) return;
setIsLoading(true);
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
setUserInput("");
Expand All @@ -420,7 +421,6 @@ export function Chat(props: {
// check if should send message
const onInputKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (shouldSubmit(e)) {
setAutoScroll(true);
onUserSubmit();
e.preventDefault();
}
Expand Down Expand Up @@ -507,13 +507,10 @@ export function Chat(props: {
return (
<div className={styles.chat} key={session.id}>
<div className={styles["window-header"]}>
<div
className={styles["window-header-title"]}
onClick={props?.showSideBar}
>
<div className={styles["window-header-title"]}>
<div
className={`${styles["window-header-main-title"]} ${styles["chat-body-title"]}`}
onClick={() => {
onClickCapture={() => {
const newTopic = prompt(Locale.Chat.Rename, session.topic);
if (newTopic && newTopic !== session.topic) {
chatStore.updateCurrentSession(
Expand All @@ -531,7 +528,7 @@ export function Chat(props: {
<div className={styles["window-actions"]}>
<div className={styles["window-action-button"] + " " + styles.mobile}>
<IconButton
icon={<MenuIcon />}
icon={<ReturnIcon />}
bordered
title={Locale.Chat.Actions.ChatList}
onClick={props?.showSideBar}
Expand Down Expand Up @@ -667,7 +664,7 @@ export function Chat(props: {
onInput={(e) => onInput(e.currentTarget.value)}
value={userInput}
onKeyDown={onInputKeyDown}
onFocus={() => setAutoScroll(isMobileScreen())}
onFocus={() => setAutoScroll(true)}
onBlur={() => {
setAutoScroll(false);
setTimeout(() => setPromptHints([]), 500);
Expand All @@ -679,7 +676,6 @@ export function Chat(props: {
text={Locale.Chat.Send}
className={styles["chat-input-send"]}
noDark
disabled={!userInput}
onClick={onUserSubmit}
/>
</div>
Expand Down
7 changes: 2 additions & 5 deletions app/components/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ function _Home() {
state.removeSession,
],
);
const chatStore = useChatStore();
const loading = !useHasHydrated();
const [showSideBar, setShowSideBar] = useState(true);

Expand Down Expand Up @@ -144,11 +145,7 @@ function _Home() {
<div className={styles["sidebar-action"] + " " + styles.mobile}>
<IconButton
icon={<CloseIcon />}
onClick={() => {
if (confirm(Locale.Home.DeleteChat)) {
removeSession(currentIndex);
}
}}
onClick={chatStore.deleteSession}
/>
</div>
<div className={styles["sidebar-action"]}>
Expand Down
20 changes: 18 additions & 2 deletions app/components/ui-lib.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,25 @@
box-shadow: var(--card-shadow);
border: var(--border-in-light);
color: var(--black);
padding: 10px 30px;
padding: 10px 20px;
border-radius: 50px;
margin-bottom: 20px;
display: flex;
align-items: center;

.toast-action {
padding-left: 20px;
color: var(--primary);
opacity: 0.8;
border: 0;
background: none;
cursor: pointer;
font-family: inherit;

&:hover {
opacity: 1;
}
}
}
}

Expand All @@ -160,4 +176,4 @@
max-height: 50vh;
}
}
}
}
28 changes: 24 additions & 4 deletions app/components/ui-lib.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,37 @@ export function showModal(props: ModalProps) {
root.render(<Modal {...props} onClose={closeModal}></Modal>);
}

export type ToastProps = { content: string };
export type ToastProps = {
content: string;
action?: {
text: string;
onClick: () => void;
};
};

export function Toast(props: ToastProps) {
return (
<div className={styles["toast-container"]}>
<div className={styles["toast-content"]}>{props.content}</div>
<div className={styles["toast-content"]}>
<span>{props.content}</span>
{props.action && (
<button
onClick={props.action.onClick}
className={styles["toast-action"]}
>
{props.action.text}
</button>
)}
</div>
</div>
);
}

export function showToast(content: string, delay = 3000) {
export function showToast(
content: string,
action?: ToastProps["action"],
delay = 3000,
) {
const div = document.createElement("div");
div.className = styles.show;
document.body.appendChild(div);
Expand All @@ -139,7 +159,7 @@ export function showToast(content: string, delay = 3000) {
close();
}, delay);

root.render(<Toast content={content} />);
root.render(<Toast content={content} action={action} />);
}

export type InputProps = React.HTMLProps<HTMLTextAreaElement> & {
Expand Down
21 changes: 21 additions & 0 deletions app/icons/return.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions app/locales/cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const cn = {
Home: {
NewChat: "新的聊天",
DeleteChat: "确认删除选中的对话?",
DeleteToast: "已删除会话",
Revert: "撤销",
},
Settings: {
Title: "设置",
Expand Down
2 changes: 2 additions & 0 deletions app/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const en: LocaleType = {
Home: {
NewChat: "New Chat",
DeleteChat: "Confirm to delete the selected conversation?",
DeleteToast: "Chat Deleted",
Revert: "Revert",
},
Settings: {
Title: "Settings",
Expand Down
2 changes: 2 additions & 0 deletions app/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const es: LocaleType = {
Home: {
NewChat: "Nuevo chat",
DeleteChat: "¿Confirmar eliminación de la conversación seleccionada?",
DeleteToast: "Chat Deleted",
Revert: "Revert",
},
Settings: {
Title: "Configuración",
Expand Down
2 changes: 2 additions & 0 deletions app/locales/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const it: LocaleType = {
Home: {
NewChat: "Nuova Chat",
DeleteChat: "Confermare la cancellazione della conversazione selezionata?",
DeleteToast: "Chat Deleted",
Revert: "Revert",
},
Settings: {
Title: "Impostazioni",
Expand Down
2 changes: 2 additions & 0 deletions app/locales/tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ const tw: LocaleType = {
Home: {
NewChat: "新的對話",
DeleteChat: "確定要刪除選取的對話嗎?",
DeleteToast: "已刪除對話",
Revert: "撤銷",
},
Settings: {
Title: "設定",
Expand Down
24 changes: 23 additions & 1 deletion app/store/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {
requestChatStream,
requestWithPrompt,
} from "../requests";
import { trimTopic } from "../utils";
import { isMobileScreen, trimTopic } from "../utils";

import Locale from "../locales";
import { showToast } from "../components/ui-lib";

export type Message = ChatCompletionResponseMessage & {
date: string;
Expand Down Expand Up @@ -204,6 +205,7 @@ interface ChatStore {
moveSession: (from: number, to: number) => void;
selectSession: (index: number) => void;
newSession: () => void;
deleteSession: () => void;
currentSession: () => ChatSession;
onNewMessage: (message: Message) => void;
onUserInput: (content: string) => Promise<void>;
Expand Down Expand Up @@ -324,6 +326,26 @@ export const useChatStore = create<ChatStore>()(
}));
},

deleteSession() {
const deletedSession = get().currentSession();
const index = get().currentSessionIndex;
const isLastSession = get().sessions.length === 1;
if (!isMobileScreen() || confirm(Locale.Home.DeleteChat)) {
get().removeSession(index);
}
showToast(Locale.Home.DeleteToast, {
text: Locale.Home.Revert,
onClick() {
set((state) => ({
sessions: state.sessions
.slice(0, index)
.concat([deletedSession])
.concat(state.sessions.slice(index + Number(isLastSession))),
}));
},
});
},

currentSession() {
let index = get().currentSessionIndex;
const sessions = get().sessions;
Expand Down
16 changes: 7 additions & 9 deletions app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,21 @@ export function trimTopic(topic: string) {
}

export async function copyToClipboard(text: string) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text).catch((err) => {
console.error("Failed to copy: ", err);
});
} else {
try {
await navigator.clipboard.writeText(text);
} catch (error) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand("copy");
console.log("Text copied to clipboard");
} catch (err) {
console.error("Failed to copy: ", err);
} catch (error) {
showToast(Locale.Copy.Failed);
}
document.body.removeChild(textArea);
} finally {
showToast(Locale.Copy.Success);
}
}

Expand Down
10 changes: 10 additions & 0 deletions docs/faq-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,13 @@ OpenAI只接受指定地区的信用卡(中国信用卡无法使用)。一

## 如何使用 Azure OpenAI 接口
请参考:[#371](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/371)

## 为什么我的 Token 消耗得这么快?
> 相关讨论:[#518](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518)
- 如果你有 GPT 4 的权限,并且日常在使用 GPT 4 api,那么由于 GPT 4 价格是 GPT 3.5 的 15 倍左右,你的账单金额会急速膨胀;
- 如果你在使用 GPT 3.5,并且使用频率并不高,仍然发现自己的账单金额在飞快增加,那么请马上按照以下步骤排查:
- 去 openai 官网查看你的 api key 消费记录,如果你的 token 每小时都有消费,并且每次都消耗了上万 token,那你的 key 一定是泄露了,请立即删除重新生成。**不要在乱七八糟的网站上查余额。**
- 如果你的密码设置很短,比如 5 位以内的字母,那么爆破成本是非常低的,建议你搜索一下 docker 的日志记录,确认是否有人大量尝试了密码组合,关键字:got access code
- 通过上述两个方法就可以定位到你的 token 被快速消耗的原因:
- 如果 openai 消费记录异常,但是 docker 日志没有问题,那么说明是 api key 泄露;
- 如果 docker 日志发现大量 got access code 爆破记录,那么就是密码被爆破了。

0 comments on commit a8a8bec

Please sign in to comment.