diff --git a/app/components/button.module.scss b/app/components/button.module.scss index 5b0bbe067..e7d5d8940 100644 --- a/app/components/button.module.scss +++ b/app/components/button.module.scss @@ -12,7 +12,7 @@ user-select: none; outline: none; border: none; - color: rgb(51, 51, 51); + color: var(--black); &[disabled] { cursor: not-allowed; diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index 8d02805f7..ab5d849f1 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -59,6 +59,7 @@ export function ChatList() { state.removeSession, state.moveSession, ]); + const chatStore = useChatStore(); const onDragEnd: OnDragEndResponder = (result) => { const { destination, source } = result; @@ -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} diff --git a/app/components/home.tsx b/app/components/home.tsx index 9e57cb870..b6a2161d6 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -93,6 +93,7 @@ function _Home() { state.removeSession, ], ); + const chatStore = useChatStore(); const loading = !useHasHydrated(); const [showSideBar, setShowSideBar] = useState(true); @@ -142,11 +143,7 @@ function _Home() {
} - onClick={() => { - if (confirm(Locale.Home.DeleteChat)) { - removeSession(currentIndex); - } - }} + onClick={chatStore.deleteSession} />
diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 95091cd0a..83eb614f7 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -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; + } + } } } @@ -160,4 +176,4 @@ max-height: 50vh; } } -} \ No newline at end of file +} diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index 6761e7f97..a72aa868f 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -110,17 +110,37 @@ export function showModal(props: ModalProps) { root.render(); } -export type ToastProps = { content: string }; +export type ToastProps = { + content: string; + action?: { + text: string; + onClick: () => void; + }; +}; export function Toast(props: ToastProps) { return (
-
{props.content}
+
+ {props.content} + {props.action && ( + + )} +
); } -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); @@ -139,7 +159,7 @@ export function showToast(content: string, delay = 3000) { close(); }, delay); - root.render(); + root.render(); } export type InputProps = React.HTMLProps & { diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 033e1c2cf..e21272a12 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -47,6 +47,8 @@ const cn = { Home: { NewChat: "新的聊天", DeleteChat: "确认删除选中的对话?", + DeleteToast: "已删除会话", + Revert: "撤销", }, Settings: { Title: "设置", diff --git a/app/locales/en.ts b/app/locales/en.ts index aefc2e57c..61d20b60f 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -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", diff --git a/app/locales/es.ts b/app/locales/es.ts index 6997d5f6d..5a83cb55c 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -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", diff --git a/app/locales/it.ts b/app/locales/it.ts index 95e6747ba..7108090eb 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -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", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 4340fe2c8..ff1794b55 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -48,6 +48,8 @@ const tw: LocaleType = { Home: { NewChat: "新的對話", DeleteChat: "確定要刪除選取的對話嗎?", + DeleteToast: "已刪除對話", + Revert: "撤銷", }, Settings: { Title: "設定", diff --git a/app/store/app.ts b/app/store/app.ts index c63fa9d4f..e72163ebb 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -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; @@ -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; @@ -324,6 +326,26 @@ export const useChatStore = create()( })); }, + 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;