forked from 77media/video-flow
新增 chatbox的新消息提示与徽记
This commit is contained in:
parent
ba897f08c1
commit
324b4cee90
@ -19,6 +19,8 @@ interface SmartChatBoxProps {
|
|||||||
onClearPreview?: () => void;
|
onClearPreview?: () => void;
|
||||||
setIsFocusChatInput?: (v: boolean) => void;
|
setIsFocusChatInput?: (v: boolean) => void;
|
||||||
aiEditingResult?: any;
|
aiEditingResult?: any;
|
||||||
|
/** 新消息回调:用于外层处理未展开时的气泡提示 */
|
||||||
|
onNewMessage?: (snippet: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MessageGroup {
|
interface MessageGroup {
|
||||||
@ -47,7 +49,8 @@ export default function SmartChatBox({
|
|||||||
previewVideoId,
|
previewVideoId,
|
||||||
onClearPreview,
|
onClearPreview,
|
||||||
setIsFocusChatInput,
|
setIsFocusChatInput,
|
||||||
aiEditingResult
|
aiEditingResult,
|
||||||
|
onNewMessage
|
||||||
}: SmartChatBoxProps) {
|
}: SmartChatBoxProps) {
|
||||||
// 消息列表引用
|
// 消息列表引用
|
||||||
const listRef = useRef<HTMLDivElement>(null);
|
const listRef = useRef<HTMLDivElement>(null);
|
||||||
@ -103,6 +106,23 @@ export default function SmartChatBox({
|
|||||||
onMessagesUpdate: handleMessagesUpdate
|
onMessagesUpdate: handleMessagesUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听消息新增,向外层抛出前10个字符的文本片段
|
||||||
|
const prevLenRef = useRef<number>(0);
|
||||||
|
useEffect(() => {
|
||||||
|
const len = messages.length;
|
||||||
|
if (len > prevLenRef.current && len > 0) {
|
||||||
|
const last = messages[len - 1];
|
||||||
|
// 提取第一个文本块
|
||||||
|
const textBlock = last.blocks.find(b => (b as any).type === 'text') as any;
|
||||||
|
const text = textBlock?.text || '';
|
||||||
|
if (text && onNewMessage) {
|
||||||
|
const snippet = text.slice(0, 20);
|
||||||
|
onNewMessage(snippet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevLenRef.current = len;
|
||||||
|
}, [messages, onNewMessage]);
|
||||||
|
|
||||||
// 监听智能剪辑结果,自动发送消息到聊天框
|
// 监听智能剪辑结果,自动发送消息到聊天框
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (aiEditingResult && isSmartChatBoxOpen) {
|
// if (aiEditingResult && isSmartChatBoxOpen) {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { MediaViewer } from "./work-flow/media-viewer";
|
|||||||
import { ThumbnailGrid } from "./work-flow/thumbnail-grid";
|
import { ThumbnailGrid } from "./work-flow/thumbnail-grid";
|
||||||
import { useWorkflowData } from "./work-flow/use-workflow-data";
|
import { useWorkflowData } from "./work-flow/use-workflow-data";
|
||||||
import { usePlaybackControls } from "./work-flow/use-playback-controls";
|
import { usePlaybackControls } from "./work-flow/use-playback-controls";
|
||||||
import { Bot, TestTube } from "lucide-react";
|
import { Bot, TestTube, MessageCircle } from "lucide-react";
|
||||||
import { GlassIconButton } from '@/components/ui/glass-icon-button';
|
import { GlassIconButton } from '@/components/ui/glass-icon-button';
|
||||||
import { SaveEditUseCase } from "@/app/service/usecase/SaveEditUseCase";
|
import { SaveEditUseCase } from "@/app/service/usecase/SaveEditUseCase";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
@ -48,6 +48,8 @@ const WorkFlow = React.memo(function WorkFlow() {
|
|||||||
const [isEditModalOpen, setIsEditModalOpen] = React.useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = React.useState(false);
|
||||||
const [activeEditTab, setActiveEditTab] = React.useState('1');
|
const [activeEditTab, setActiveEditTab] = React.useState('1');
|
||||||
const [isSmartChatBoxOpen, setIsSmartChatBoxOpen] = React.useState(true);
|
const [isSmartChatBoxOpen, setIsSmartChatBoxOpen] = React.useState(true);
|
||||||
|
const [chatTip, setChatTip] = React.useState<string | null>(null);
|
||||||
|
const [hasUnread, setHasUnread] = React.useState(false);
|
||||||
const [previewVideoUrl, setPreviewVideoUrl] = React.useState<string | null>(null);
|
const [previewVideoUrl, setPreviewVideoUrl] = React.useState<string | null>(null);
|
||||||
const [previewVideoId, setPreviewVideoId] = React.useState<string | null>(null);
|
const [previewVideoId, setPreviewVideoId] = React.useState<string | null>(null);
|
||||||
const [isFocusChatInput, setIsFocusChatInput] = React.useState(false);
|
const [isFocusChatInput, setIsFocusChatInput] = React.useState(false);
|
||||||
@ -617,15 +619,30 @@ Please process this video editing request.`;
|
|||||||
|
|
||||||
{/* 智能对话按钮 */}
|
{/* 智能对话按钮 */}
|
||||||
<div
|
<div
|
||||||
className="fixed right-[1rem] bottom-[10rem] z-[49]"
|
className={`fixed right-[1rem] z-[49] ${isMobile ? 'bottom-[2rem]' : 'bottom-[10rem]'}`}
|
||||||
>
|
>
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
<GlassIconButton
|
<div className="relative">
|
||||||
icon={Bot}
|
{(!isSmartChatBoxOpen && chatTip) && (
|
||||||
size='md'
|
<div className="absolute -top-8 right-0 bg-black/80 text-white text-xs px-2 py-1 rounded-md whitespace-nowrap">
|
||||||
onClick={() => setIsSmartChatBoxOpen(true)}
|
{chatTip}
|
||||||
className="backdrop-blur-lg"
|
</div>
|
||||||
/>
|
)}
|
||||||
|
{/* 红点徽标 */}
|
||||||
|
{(!isSmartChatBoxOpen && hasUnread) && (
|
||||||
|
<span className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border border-white" />
|
||||||
|
)}
|
||||||
|
<GlassIconButton
|
||||||
|
icon={MessageCircle}
|
||||||
|
size='md'
|
||||||
|
onClick={() => {
|
||||||
|
setIsSmartChatBoxOpen(true);
|
||||||
|
setChatTip(null);
|
||||||
|
setHasUnread(false);
|
||||||
|
}}
|
||||||
|
className="backdrop-blur-lg bg-custom-purple border-transparent hover:opacity-90"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip title="Open chat" placement="left">
|
<Tooltip title="Open chat" placement="left">
|
||||||
<GlassIconButton
|
<GlassIconButton
|
||||||
@ -641,6 +658,7 @@ Please process this video editing request.`;
|
|||||||
{/* 智能对话弹窗 */}
|
{/* 智能对话弹窗 */}
|
||||||
<Drawer
|
<Drawer
|
||||||
width={isMobile ? '100vw' : '25%'}
|
width={isMobile ? '100vw' : '25%'}
|
||||||
|
height={isMobile ? 'auto' : ''}
|
||||||
placement={isMobile ? 'bottom' : 'right'}
|
placement={isMobile ? 'bottom' : 'right'}
|
||||||
closable={false}
|
closable={false}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
@ -648,9 +666,9 @@ Please process this video editing request.`;
|
|||||||
getContainer={false}
|
getContainer={false}
|
||||||
autoFocus={false}
|
autoFocus={false}
|
||||||
mask={false}
|
mask={false}
|
||||||
zIndex={49}
|
zIndex={60}
|
||||||
rootClassName="outline-none"
|
rootClassName="outline-none"
|
||||||
className="backdrop-blur-lg bg-black/30 border border-white/20 shadow-xl"
|
className="backdrop-blur-lg bg-black/30 border border-white/20 shadow-xl max-h-[90vh]"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
...(isMobile
|
...(isMobile
|
||||||
@ -676,6 +694,14 @@ Please process this video editing request.`;
|
|||||||
previewVideoUrl={previewVideoUrl}
|
previewVideoUrl={previewVideoUrl}
|
||||||
previewVideoId={previewVideoId}
|
previewVideoId={previewVideoId}
|
||||||
setIsFocusChatInput={setIsFocusChatInput}
|
setIsFocusChatInput={setIsFocusChatInput}
|
||||||
|
onNewMessage={(snippet) => {
|
||||||
|
if (!isSmartChatBoxOpen && snippet) {
|
||||||
|
setChatTip(snippet);
|
||||||
|
setHasUnread(true);
|
||||||
|
// 3秒后自动消失
|
||||||
|
setTimeout(() => setChatTip(null), 3000);
|
||||||
|
}
|
||||||
|
}}
|
||||||
onClearPreview={() => {
|
onClearPreview={() => {
|
||||||
setPreviewVideoUrl(null);
|
setPreviewVideoUrl(null);
|
||||||
setPreviewVideoId(null);
|
setPreviewVideoId(null);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user