fix(chat): add history loading skeletons
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import Image from "next/image";
|
||||
import React from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { Box, Paper, Stack, Typography, alpha, useTheme, Grid } from "@mui/material";
|
||||
import { Box, Paper, Skeleton, Stack, Typography, alpha, useTheme, Grid } from "@mui/material";
|
||||
import WaterDropRounded from "@mui/icons-material/WaterDropRounded";
|
||||
import SensorsRounded from "@mui/icons-material/SensorsRounded";
|
||||
import TroubleshootRounded from "@mui/icons-material/TroubleshootRounded";
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
type AgentWorkspaceProps = {
|
||||
messages: Message[];
|
||||
isStreaming: boolean;
|
||||
isLoadingSession?: boolean;
|
||||
bottomRef: React.RefObject<HTMLDivElement | null>;
|
||||
speakingMessageId: string | null;
|
||||
speechState: SpeechState;
|
||||
@@ -226,9 +227,72 @@ const EmptyState = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const SessionLoadingSkeleton = () => (
|
||||
<Stack
|
||||
spacing={2.25}
|
||||
aria-label="正在加载历史记录"
|
||||
sx={{ width: "100%", maxWidth: 760, alignSelf: "stretch" }}
|
||||
>
|
||||
{Array.from({ length: 2 }, (_, turnIndex) => (
|
||||
<Stack key={turnIndex} spacing={1.25}>
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
width: turnIndex === 0 ? "72%" : "64%",
|
||||
maxWidth: "86%",
|
||||
p: 1.75,
|
||||
borderRadius: 5,
|
||||
borderBottomRightRadius: 2,
|
||||
bgcolor: alpha("#00acc1", 0.16),
|
||||
border: `1px solid ${alpha("#00acc1", 0.12)}`,
|
||||
boxShadow: `0 8px 24px -12px ${alpha("#00acc1", 0.35)}`,
|
||||
}}
|
||||
>
|
||||
<Stack spacing={0.85}>
|
||||
<Skeleton variant="text" width="76%" height={18} />
|
||||
<Skeleton variant="text" width="48%" height={15} />
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" spacing={1.5} alignItems="flex-start">
|
||||
<Skeleton
|
||||
variant="circular"
|
||||
width={34}
|
||||
height={34}
|
||||
sx={{ bgcolor: alpha("#00acc1", 0.12), flexShrink: 0, mt: 0.25 }}
|
||||
/>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
p: 2,
|
||||
borderRadius: 5,
|
||||
bgcolor: alpha("#ffffff", 0.52),
|
||||
border: `1px solid ${alpha("#fff", 0.72)}`,
|
||||
boxShadow: `0 10px 30px -10px ${alpha("#000", 0.06)}`,
|
||||
}}
|
||||
>
|
||||
<Stack spacing={1}>
|
||||
<Skeleton variant="text" width="38%" height={16} />
|
||||
<Skeleton variant="text" width="94%" height={16} />
|
||||
<Skeleton variant="text" width={turnIndex === 0 ? "88%" : "82%"} height={16} />
|
||||
<Skeleton variant="text" width={turnIndex === 0 ? "78%" : "70%"} height={16} />
|
||||
<Skeleton variant="rounded" width="100%" height={turnIndex === 0 ? 104 : 76} sx={{ borderRadius: 2 }} />
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
export const AgentWorkspace = ({
|
||||
messages,
|
||||
isStreaming,
|
||||
isLoadingSession = false,
|
||||
bottomRef,
|
||||
speakingMessageId,
|
||||
speechState,
|
||||
@@ -270,49 +334,55 @@ export const AgentWorkspace = ({
|
||||
zIndex: 5,
|
||||
}}
|
||||
>
|
||||
<AnimatePresence initial={false}>
|
||||
{messages.length === 0 ? <EmptyState /> : null}
|
||||
</AnimatePresence>
|
||||
{isLoadingSession ? (
|
||||
<SessionLoadingSkeleton />
|
||||
) : (
|
||||
<>
|
||||
<AnimatePresence initial={false}>
|
||||
{messages.length === 0 ? <EmptyState /> : null}
|
||||
</AnimatePresence>
|
||||
|
||||
{messages.length > 0 ? (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||
<TurnList
|
||||
messages={historyMessages}
|
||||
isStreaming={isStreaming}
|
||||
speakingMessageId={speakingMessageId}
|
||||
speechState={speechState}
|
||||
onSpeak={onSpeak}
|
||||
onPauseSpeech={onPauseSpeech}
|
||||
onResumeSpeech={onResumeSpeech}
|
||||
onStopSpeech={onStopSpeech}
|
||||
isTtsSupported={isTtsSupported}
|
||||
onCreateBranch={onCreateBranch}
|
||||
onReplyPermission={onReplyPermission}
|
||||
onReplyQuestion={onReplyQuestion}
|
||||
onRejectQuestion={onRejectQuestion}
|
||||
/>
|
||||
{messages.length > 0 ? (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||
<TurnList
|
||||
messages={historyMessages}
|
||||
isStreaming={isStreaming}
|
||||
speakingMessageId={speakingMessageId}
|
||||
speechState={speechState}
|
||||
onSpeak={onSpeak}
|
||||
onPauseSpeech={onPauseSpeech}
|
||||
onResumeSpeech={onResumeSpeech}
|
||||
onStopSpeech={onStopSpeech}
|
||||
isTtsSupported={isTtsSupported}
|
||||
onCreateBranch={onCreateBranch}
|
||||
onReplyPermission={onReplyPermission}
|
||||
onReplyQuestion={onReplyQuestion}
|
||||
onRejectQuestion={onRejectQuestion}
|
||||
/>
|
||||
|
||||
{streamingMessage ? (
|
||||
<TurnList
|
||||
messages={[streamingMessage]}
|
||||
isStreaming={isStreaming}
|
||||
speakingMessageId={speakingMessageId}
|
||||
speechState={speechState}
|
||||
onSpeak={onSpeak}
|
||||
onPauseSpeech={onPauseSpeech}
|
||||
onResumeSpeech={onResumeSpeech}
|
||||
onStopSpeech={onStopSpeech}
|
||||
isTtsSupported={isTtsSupported}
|
||||
onCreateBranch={onCreateBranch}
|
||||
onReplyPermission={onReplyPermission}
|
||||
onReplyQuestion={onReplyQuestion}
|
||||
onRejectQuestion={onRejectQuestion}
|
||||
/>
|
||||
{streamingMessage ? (
|
||||
<TurnList
|
||||
messages={[streamingMessage]}
|
||||
isStreaming={isStreaming}
|
||||
speakingMessageId={speakingMessageId}
|
||||
speechState={speechState}
|
||||
onSpeak={onSpeak}
|
||||
onPauseSpeech={onPauseSpeech}
|
||||
onResumeSpeech={onResumeSpeech}
|
||||
onStopSpeech={onStopSpeech}
|
||||
isTtsSupported={isTtsSupported}
|
||||
onCreateBranch={onCreateBranch}
|
||||
onReplyPermission={onReplyPermission}
|
||||
onReplyQuestion={onReplyQuestion}
|
||||
onRejectQuestion={onRejectQuestion}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
) : null}
|
||||
</Box>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
|
||||
{showTypingIndicator ? (
|
||||
{!isLoadingSession && showTypingIndicator ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.94 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
|
||||
Reference in New Issue
Block a user