From 92ff0bbef43b6f34e776e6e1b734bd00ffecb14e Mon Sep 17 00:00:00 2001
From: Jagger <634750802@qq.com>
Date: Thu, 19 Sep 2024 10:35:58 +0800
Subject: [PATCH] style(frontend): optimize message view (#293)
close #287
close #288
close #289
close #291
---
.github/workflows/release.yml | 5 ++
e2e/prepare-test.sh | 5 ++
e2e/tests/chat.spec.ts | 2 +-
e2e/utils/chat.ts | 2 +-
.../app/src/components/chat/chat-hooks.tsx | 4 +
.../chat/conversation-message-groups.tsx | 2 +-
.../app/src/components/chat/conversation.tsx | 2 +-
.../src/components/chat/message-answer.tsx | 4 +-
.../components/chat/message-beta-alert.tsx | 16 ++++
.../src/components/chat/message-feedback.tsx | 18 +++--
.../app/src/components/chat/message-input.tsx | 6 +-
.../components/chat/message-operations.tsx | 81 +++++++++++++++----
frontend/app/src/components/ui/alert.tsx | 2 +
frontend/app/src/components/ui/tooltip.tsx | 4 +-
frontend/app/src/components/use-size.ts | 6 +-
frontend/packages/widget-react/package.json | 1 +
frontend/packages/widget-react/src/Widget.tsx | 8 +-
frontend/pnpm-lock.yaml | 3 +
18 files changed, 133 insertions(+), 38 deletions(-)
create mode 100755 e2e/prepare-test.sh
create mode 100644 frontend/app/src/components/chat/message-beta-alert.tsx
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d3aaa81b..9389cfa5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -157,6 +157,11 @@ jobs:
- name: Install Playwright Browsers
run: npx playwright install --with-deps
+ - name: Fetch Images
+ run: ./prepare-test.sh
+ env:
+ E2E_DOCKER_TAG: sha-${{ github.sha }}-dev
+
- name: Run tests
run: ./start-test.sh
env:
diff --git a/e2e/prepare-test.sh b/e2e/prepare-test.sh
new file mode 100755
index 00000000..af0dfc3d
--- /dev/null
+++ b/e2e/prepare-test.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -e
+
+docker compose pull frontend background backend tidb redis static-web-server
diff --git a/e2e/tests/chat.spec.ts b/e2e/tests/chat.spec.ts
index f20312cc..d0da6b13 100644
--- a/e2e/tests/chat.spec.ts
+++ b/e2e/tests/chat.spec.ts
@@ -12,7 +12,7 @@ test.describe('Chat', () => {
// https://playwright.dev/docs/events#waiting-for-event
const chatRequestPromise = getChatRequestPromise(page, baseURL);
- const trigger = page.locator('button', { has: page.locator('svg.lucide-arrow-right') });
+ const trigger = page.locator('button', { has: page.locator('svg.lucide-arrow-up') });
await trigger.click();
await expect(trigger).toBeDisabled();
diff --git a/e2e/utils/chat.ts b/e2e/utils/chat.ts
index 291df694..4fec706b 100644
--- a/e2e/utils/chat.ts
+++ b/e2e/utils/chat.ts
@@ -20,7 +20,7 @@ export async function testNewChat (page: Page, chatRequest: Request, validatePag
expect(chatResponse.ok()).toBe(true);
// Feedback button indicates chat ends.
- await page.locator('button', { has: page.locator('svg.lucide-message-square-plus') }).waitFor({ state: 'visible' });
+ await page.locator('button', { has: page.locator('svg.lucide-thumbs-up') }).waitFor({ state: 'visible' });
return await chatResponse.text();
});
diff --git a/frontend/app/src/components/chat/chat-hooks.tsx b/frontend/app/src/components/chat/chat-hooks.tsx
index 4990f398..1f6adc63 100644
--- a/frontend/app/src/components/chat/chat-hooks.tsx
+++ b/frontend/app/src/components/chat/chat-hooks.tsx
@@ -83,6 +83,7 @@ export function useChats () {
export interface ChatMessageGroup {
user: ChatMessageController;
assistant: ChatMessageController | undefined;
+ hasFirstAssistantMessage: boolean;
}
export function useChatController (
@@ -201,6 +202,7 @@ export function useChatMessageGroups (controllers: ChatMessageController[]) {
function collectMessageGroups (messageControllers: ChatMessageController[]) {
const groups: ChatMessageGroup[] = [];
+ let hasAssistant: boolean = false;
let user: ChatMessageController | undefined;
for (let messageController of messageControllers) {
@@ -213,7 +215,9 @@ function collectMessageGroups (messageControllers: ChatMessageController[]) {
groups.push({
user,
assistant: messageController,
+ hasFirstAssistantMessage: !hasAssistant,
});
+ hasAssistant = true;
} else {
console.warn('No matched user message, drop assistant message', messageController.message.id);
}
diff --git a/frontend/app/src/components/chat/conversation-message-groups.tsx b/frontend/app/src/components/chat/conversation-message-groups.tsx
index c531ccf8..d5674a34 100644
--- a/frontend/app/src/components/chat/conversation-message-groups.tsx
+++ b/frontend/app/src/components/chat/conversation-message-groups.tsx
@@ -106,7 +106,7 @@ function ConversationMessageGroup ({ group }: { group: ChatMessageGroup }) {
-
+
{group.assistant && }
diff --git a/frontend/app/src/components/chat/conversation.tsx b/frontend/app/src/components/chat/conversation.tsx
index dbbeaaf7..6d456b85 100644
--- a/frontend/app/src/components/chat/conversation.tsx
+++ b/frontend/app/src/components/chat/conversation.tsx
@@ -71,7 +71,7 @@ export function Conversation ({ open, chat, chatId, history, placeholder, preven
- {size && open && }
diff --git a/frontend/app/src/components/chat/message-answer.tsx b/frontend/app/src/components/chat/message-answer.tsx
index eef7ee78..b7081d8b 100644
--- a/frontend/app/src/components/chat/message-answer.tsx
+++ b/frontend/app/src/components/chat/message-answer.tsx
@@ -1,9 +1,10 @@
import { useChatMessageField, useChatMessageStreamContainsState } from '@/components/chat/chat-hooks';
import type { ChatMessageController } from '@/components/chat/chat-message-controller';
import { AppChatStreamState } from '@/components/chat/chat-stream-state';
+import { MessageBetaAlert } from '@/components/chat/message-beta-alert';
import { MessageContent } from '@/components/chat/message-content';
-export function MessageAnswer ({ message }: { message: ChatMessageController | undefined }) {
+export function MessageAnswer ({ message, showBetaAlert }: { message: ChatMessageController | undefined, showBetaAlert?: boolean }) {
const content = useChatMessageField(message, 'content');
const shouldShow = useChatMessageStreamContainsState(message, AppChatStreamState.GENERATE_ANSWER);
@@ -26,6 +27,7 @@ export function MessageAnswer ({ message }: { message: ChatMessageController | u
Answer
+ {showBetaAlert && }
>
);
diff --git a/frontend/app/src/components/chat/message-beta-alert.tsx b/frontend/app/src/components/chat/message-beta-alert.tsx
new file mode 100644
index 00000000..dfa757b0
--- /dev/null
+++ b/frontend/app/src/components/chat/message-beta-alert.tsx
@@ -0,0 +1,16 @@
+import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
+import { FlaskConicalIcon } from 'lucide-react';
+
+export function MessageBetaAlert () {
+ return (
+
+
+
+ This chatbot is in Beta.
+
+
+ All generated information should be verified prior to use.
+
+
+ );
+}
diff --git a/frontend/app/src/components/chat/message-feedback.tsx b/frontend/app/src/components/chat/message-feedback.tsx
index 7b12b675..41a51254 100644
--- a/frontend/app/src/components/chat/message-feedback.tsx
+++ b/frontend/app/src/components/chat/message-feedback.tsx
@@ -1,20 +1,26 @@
import type { FeedbackParams } from '@/api/chats';
import { usePortalContainer } from '@/components/portal-provider';
import { Button } from '@/components/ui/button';
-import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Textarea } from '@/components/ui/textarea';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { Loader2Icon, ThumbsDownIcon, ThumbsUpIcon } from 'lucide-react';
-import { type ReactElement, useEffect, useState } from 'react';
+import { type ReactNode, useEffect, useState } from 'react';
-export function MessageFeedback ({ initial, onFeedback, children }: { initial?: FeedbackParams, onFeedback: (action: 'like' | 'dislike', comment: string) => Promise, children: ReactElement }) {
+export function MessageFeedback ({ initial, onFeedback, defaultAction, children }: { initial?: FeedbackParams, defaultAction?: 'like' | 'dislike', onFeedback: (action: 'like' | 'dislike', comment: string) => Promise, children: ReactNode }) {
const [open, setOpen] = useState(false);
- const [action, setAction] = useState<'like' | 'dislike'>(initial?.feedback_type ?? 'like');
+ const [action, setAction] = useState<'like' | 'dislike'>(initial?.feedback_type ?? defaultAction ?? 'like');
// const [detail, setDetail] = useState>(() => (initial ?? {}));
const [comment, setComment] = useState(initial?.comment ?? '');
const [running, setRunning] = useState(false);
const [deleting, setDeleting] = useState(false);
+ useEffect(() => {
+ if (defaultAction && !initial) {
+ setAction(defaultAction);
+ }
+ }, [defaultAction, initial]);
+
useEffect(() => {
if (initial) {
setAction(initial.feedback_type);
@@ -30,9 +36,7 @@ export function MessageFeedback ({ initial, onFeedback, children }: { initial?:
return (