mirror of https://github.com/buster-so/buster.git
update how we pass around on click events
This commit is contained in:
parent
34c6f6840f
commit
ab58050270
|
@ -77,6 +77,7 @@ export const useGetChat = <TData = IBusterChat>(
|
|||
if (lastMessage) {
|
||||
Object.values(lastMessage.response_messages).forEach((responseMessage) => {
|
||||
if (responseMessage.type === 'file' && responseMessage.file_type === 'metric') {
|
||||
console.log('prefetching metric', responseMessage.id);
|
||||
prefetchGetMetricDataClient({ id: responseMessage.id }, queryClient);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -54,6 +54,7 @@ const useGetDashboardAndInitializeMetrics = () => {
|
|||
const prevMetric = queryClient.getQueryData(queryKeys.metricsGetMetric(metric.id).queryKey);
|
||||
const upgradedMetric = upgradeMetricToIMetric(metric, prevMetric);
|
||||
queryClient.setQueryData(queryKeys.metricsGetMetric(metric.id).queryKey, upgradedMetric);
|
||||
console.log('prefetching metric', metric.id);
|
||||
prefetchGetMetricDataClient({ id: metric.id }, queryClient);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,6 +3,9 @@ import { upgradeMetricToIMetric } from '@/lib/metrics';
|
|||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { getMetric_server, listMetrics, listMetrics_server } from './requests';
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
export const prefetchGetMetric = async (
|
||||
params: Parameters<typeof getMetric_server>[0],
|
||||
queryClientProp?: QueryClient
|
||||
|
|
|
@ -22,7 +22,6 @@ import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsPro
|
|||
import { useGetUserFavorites } from '../users';
|
||||
import type { IBusterMetric } from '@/api/asset_interfaces/metric';
|
||||
import { create } from 'mutative';
|
||||
import { isServer } from '@tanstack/react-query';
|
||||
import {
|
||||
useAddAssetToCollection,
|
||||
useRemoveAssetFromCollection
|
||||
|
@ -106,7 +105,6 @@ export const useGetMetricData = ({
|
|||
version_number?: number;
|
||||
}) => {
|
||||
const searchParams = useSearchParams();
|
||||
const queryClient = useQueryClient();
|
||||
const queryVersionNumber = searchParams.get('metric_version_number');
|
||||
|
||||
const version_number = useMemo(() => {
|
||||
|
@ -114,12 +112,9 @@ export const useGetMetricData = ({
|
|||
}, [version_number_prop, queryVersionNumber]);
|
||||
|
||||
const queryFn = useMemoizedFn(() => {
|
||||
console.log('hit!', isServer, metricsQueryKeys.metricsGetData(id, version_number).queryKey);
|
||||
return getMetricData({ id, version_number });
|
||||
});
|
||||
|
||||
console.log('data here!');
|
||||
|
||||
return useQuery({
|
||||
...metricsQueryKeys.metricsGetData(id, version_number),
|
||||
queryFn
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
'use server';
|
||||
|
||||
import { MetricController } from '@/controllers/MetricController';
|
||||
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
||||
|
||||
export default async function MetricPage(props: { params: Promise<{ metricId: string }> }) {
|
||||
const [params] = await Promise.all([props.params]);
|
||||
const params = await props.params;
|
||||
|
||||
const { metricId } = params;
|
||||
|
||||
return (
|
||||
|
|
|
@ -34,7 +34,7 @@ export default function Page({
|
|||
|
||||
const onClearSelectedDataSource = useMemoizedFn(() => {
|
||||
setSelectedDataSource(null);
|
||||
onChangePage(createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES_ADD }));
|
||||
onChangePage({ route: BusterRoutes.SETTINGS_DATASOURCES_ADD });
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -55,7 +55,7 @@ export const NewDatasetModal: React.FC<{
|
|||
});
|
||||
|
||||
const onAddDataSourceClick = useMemoizedFn(() => {
|
||||
onChangePage(createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES_ADD }));
|
||||
onChangePage({ route: BusterRoutes.SETTINGS_DATASOURCES_ADD });
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
}, 450);
|
||||
|
|
|
@ -38,7 +38,6 @@ export const VersionHistoryPanel = React.memo(
|
|||
|
||||
return (
|
||||
<AppPageLayout
|
||||
headerClassName="border-l"
|
||||
header={useMemo(
|
||||
() => (
|
||||
<PanelHeader removeVersionHistoryQueryParams={removeVersionHistoryQueryParams} />
|
||||
|
|
|
@ -40,10 +40,7 @@ export const AppPageLayout: React.FC<
|
|||
)}>
|
||||
{header && (
|
||||
<AppPageLayoutHeader
|
||||
className={cn(
|
||||
headerBorderVariant === 'ghost' && 'relative z-10 -mt-[1px]',
|
||||
headerClassName
|
||||
)}
|
||||
className={cn(headerBorderVariant === 'ghost' && '-mt-[0.5px]', headerClassName)}
|
||||
sizeVariant={headerSizeVariant}
|
||||
borderVariant={headerBorderVariant}>
|
||||
{header}
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
const headerVariants = cva(
|
||||
'bg-page-background flex max-h-[38px] min-h-[38px] items-center z-10 justify-between gap-x-2.5 relative',
|
||||
'bg-page-background flex max-h-[38px] min-h-[38px] items-center justify-between gap-x-2.5 relative',
|
||||
{
|
||||
variants: {
|
||||
sizeVariant: {
|
||||
|
|
|
@ -48,8 +48,7 @@ export const Default: Story = {
|
|||
args: {
|
||||
isSelectedFile: false,
|
||||
isCompletedStream: true,
|
||||
responseMessage: mockResponseMessage,
|
||||
onClick: fn()
|
||||
responseMessage: mockResponseMessage
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -57,8 +56,7 @@ export const Selected: Story = {
|
|||
args: {
|
||||
isSelectedFile: true,
|
||||
isCompletedStream: true,
|
||||
responseMessage: mockResponseMessage,
|
||||
onClick: fn()
|
||||
responseMessage: mockResponseMessage
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -66,8 +64,7 @@ export const Streaming: Story = {
|
|||
args: {
|
||||
isSelectedFile: false,
|
||||
isCompletedStream: false,
|
||||
responseMessage: mockResponseMessage,
|
||||
onClick: fn()
|
||||
responseMessage: mockResponseMessage
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -78,7 +75,6 @@ export const LongFileName: Story = {
|
|||
responseMessage: {
|
||||
...mockResponseMessage,
|
||||
file_name: 'very_long_file_name_that_should_truncate_in_the_ui.txt'
|
||||
},
|
||||
onClick: fn()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,10 +12,9 @@ import { cn } from '@/lib/classMerge';
|
|||
|
||||
export const StreamingMessage_File: React.FC<{
|
||||
isSelectedFile: boolean;
|
||||
onClick: () => void;
|
||||
responseMessage: BusterChatResponseMessage_file;
|
||||
isCompletedStream: boolean;
|
||||
}> = React.memo(({ isCompletedStream, responseMessage, onClick, isSelectedFile }) => {
|
||||
}> = React.memo(({ isCompletedStream, responseMessage, isSelectedFile }) => {
|
||||
const {
|
||||
file_name,
|
||||
version_number,
|
||||
|
@ -28,7 +27,6 @@ export const StreamingMessage_File: React.FC<{
|
|||
<motion.div
|
||||
id={id}
|
||||
{...itemAnimationConfig}
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'border-border hover:border-text-tertiary flex cursor-pointer flex-col items-center overflow-hidden rounded border transition-all duration-200 hover:shadow',
|
||||
isSelectedFile && 'border-black shadow'
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from './chatStreamMessageHelper';
|
||||
import { useGetChatMemoized } from '@/api/buster_rest/chats';
|
||||
import { useChatUpdate } from './useChatUpdate';
|
||||
import { prefetchGetMetricDataClient } from '@/api/buster_rest/metrics';
|
||||
import { prefetchGetMetricDataClient, prefetchGetMetric } from '@/api/buster_rest/metrics';
|
||||
|
||||
export const useChatStreamMessage = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
@ -62,7 +62,20 @@ export const useChatStreamMessage = () => {
|
|||
const queryKey = options.queryKey;
|
||||
queryClient.setQueryData(queryKey, message);
|
||||
chatRefMessages.current[message.id] = message;
|
||||
prefetchGetMetricDataClient({ id: message.id }, queryClient);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const prefetchLastMessageMetricData = useMemoizedFn(
|
||||
(iChat: IBusterChat, iChatMessages: Record<string, IBusterChatMessage>) => {
|
||||
const lastMessageId = iChat.message_ids[iChat.message_ids.length - 1];
|
||||
const lastMessage = iChatMessages[lastMessageId];
|
||||
if (lastMessage?.response_message_ids) {
|
||||
Object.values(lastMessage.response_messages).forEach((responseMessage) => {
|
||||
if (responseMessage.type === 'file' && responseMessage.file_type === 'metric') {
|
||||
prefetchGetMetricDataClient({ id: responseMessage.id }, queryClient);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -73,6 +86,7 @@ export const useChatStreamMessage = () => {
|
|||
normalizeChatMessage(iChatMessages);
|
||||
onUpdateChat(iChat);
|
||||
removeBlackBoxMessage({ messageId: iChat.message_ids[iChat.message_ids.length - 1] });
|
||||
prefetchLastMessageMetricData(iChat, iChatMessages);
|
||||
});
|
||||
|
||||
const stopChatCallback = useMemoizedFn((chatId: string) => {
|
||||
|
|
|
@ -10,6 +10,11 @@ import { FileIndeterminateLoader } from '@/components/features/FileIndeterminate
|
|||
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
|
||||
import { MetricViewError } from './MetricViewError';
|
||||
|
||||
/*
|
||||
TODO: consider makiing this a server component that fetches the metric and metric data?
|
||||
As long as we have a loading.tsx component that can handle the loading state, this should work?
|
||||
*/
|
||||
|
||||
export const MetricController: React.FC<{
|
||||
metricId: string;
|
||||
}> = React.memo(({ metricId }) => {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import { Text, Title } from '@/components/ui/typography';
|
||||
import { StatusCard } from '@/components/ui/card/StatusCard';
|
||||
|
||||
export const MetricViewError: React.FC<{ error: string | undefined }> = ({
|
||||
|
|
|
@ -13,7 +13,6 @@ interface ReasoningControllerProps {
|
|||
|
||||
export const ReasoningController: React.FC<ReasoningControllerProps> = ({ chatId, messageId }) => {
|
||||
const { data: hasChat } = useGetChat({ id: chatId || '' }, (x) => !!x.id);
|
||||
|
||||
const reasoningMessageIds = useGetChatMessage(messageId, (x) => x?.reasoning_message_ids);
|
||||
const isCompletedStream = useGetChatMessage(messageId, (x) => x?.isCompletedStream);
|
||||
|
||||
|
|
|
@ -2,31 +2,46 @@ import type { FileType } from '@/api/asset_interfaces';
|
|||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
import { ArrowUpRight } from '@/components/ui/icons';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout/ChatLayoutContext';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import Link from 'next/link';
|
||||
import { createChatAssetRoute } from '@/layouts/ChatLayout/ChatLayoutContext/helpers';
|
||||
|
||||
export const ReasoningFileButtons = React.memo(
|
||||
({ fileType, fileId, type }: { fileType: FileType; fileId: string; type: 'file' | 'status' }) => {
|
||||
const onSetSelectedFile = useChatLayoutContextSelector((state) => state.onSetSelectedFile);
|
||||
|
||||
const onOpenFile = useMemoizedFn((e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onSetSelectedFile({
|
||||
id: fileId,
|
||||
({
|
||||
fileType,
|
||||
fileId,
|
||||
type,
|
||||
chatId
|
||||
}: {
|
||||
fileType: FileType;
|
||||
fileId: string;
|
||||
type: 'file' | 'status';
|
||||
chatId: string;
|
||||
}) => {
|
||||
const href = useMemo(() => {
|
||||
return createChatAssetRoute({
|
||||
chatId: chatId,
|
||||
assetId: fileId,
|
||||
type: fileType
|
||||
});
|
||||
});
|
||||
}, [chatId, fileId, fileType]);
|
||||
|
||||
if (type === 'status') return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AppTooltip title="Open file" sideOffset={12}>
|
||||
<Button onClick={onOpenFile} variant="ghost" prefix={<ArrowUpRight />}></Button>
|
||||
</AppTooltip>
|
||||
</div>
|
||||
<AppTooltip title="Open file" sideOffset={12}>
|
||||
<Link
|
||||
href={href || ''}
|
||||
prefetch
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<Button variant="ghost" prefix={<ArrowUpRight />} />
|
||||
</Link>
|
||||
</AppTooltip>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ export const ReasoningMessage_File: React.FC<ReasoningMessageFileProps> = React.
|
|||
const buttons = !isCompletedStream ? (
|
||||
<StreamingMessageStatus status={status} />
|
||||
) : (
|
||||
<ReasoningFileButtons fileType={file_type} fileId={version_id} type="file" />
|
||||
<ReasoningFileButtons fileType={file_type} chatId={chatId} fileId={version_id} type="file" />
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,13 +6,18 @@ import { ReasoningMessagePills } from './ReasoningMessagePills';
|
|||
export const ReasoningMessagePillContainer: React.FC<{
|
||||
pillContainer: BusterChatMessageReasoning_pillContainer;
|
||||
isCompletedStream: boolean;
|
||||
}> = React.memo(({ pillContainer, isCompletedStream }) => {
|
||||
chatId: string;
|
||||
}> = React.memo(({ pillContainer, isCompletedStream, chatId }) => {
|
||||
return (
|
||||
<div className="flex flex-col space-y-1">
|
||||
<Text size="xs" variant="tertiary">
|
||||
{pillContainer.title}
|
||||
</Text>
|
||||
<ReasoningMessagePills pills={pillContainer.pills} isCompletedStream={isCompletedStream} />
|
||||
<ReasoningMessagePills
|
||||
chatId={chatId}
|
||||
pills={pillContainer.pills}
|
||||
isCompletedStream={isCompletedStream}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import type { BusterChatMessageReasoning_pill } from '@/api/asset_interfaces/chat';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { BusterChatMessageReasoning_pill, FileType } from '@/api/asset_interfaces/chat';
|
||||
import React from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { Popover } from '@/components/ui/tooltip/Popover';
|
||||
import {
|
||||
isOpenableFile,
|
||||
useChatLayoutContextSelector
|
||||
} from '@/layouts/ChatLayout/ChatLayoutContext';
|
||||
import { type SelectedFile } from '@/layouts/ChatLayout/interfaces';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import Link from 'next/link';
|
||||
import { createChatAssetRoute } from '@/layouts/ChatLayout/ChatLayoutContext/helpers';
|
||||
|
||||
const duration = 0.25;
|
||||
|
||||
|
@ -44,24 +41,19 @@ const pillVariants = {
|
|||
export const ReasoningMessagePills: React.FC<{
|
||||
pills: BusterChatMessageReasoning_pill[];
|
||||
isCompletedStream: boolean;
|
||||
}> = React.memo(({ pills = [], isCompletedStream }) => {
|
||||
const onSetSelectedFile = useChatLayoutContextSelector((x) => x.onSetSelectedFile);
|
||||
|
||||
chatId: string;
|
||||
}> = React.memo(({ pills = [], isCompletedStream, chatId }) => {
|
||||
const useAnimation = !isCompletedStream;
|
||||
|
||||
const handlePillClick = useMemoizedFn(
|
||||
(pill: Pick<BusterChatMessageReasoning_pill, 'id' | 'type'>) => {
|
||||
if (isOpenableFile(pill.type)) {
|
||||
onSetSelectedFile(pill as SelectedFile);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const isClickablePill = useMemo(() => {
|
||||
return pills.some((pill) => isOpenableFile(pill.type));
|
||||
}, [pills]);
|
||||
|
||||
const onClick = isClickablePill ? handlePillClick : undefined;
|
||||
const makeHref = useMemoizedFn((pill: Pick<BusterChatMessageReasoning_pill, 'id' | 'type'>) => {
|
||||
const link = createChatAssetRoute({
|
||||
chatId: chatId,
|
||||
assetId: pill.id,
|
||||
type: pill.type as FileType
|
||||
});
|
||||
if (link) return link;
|
||||
return '';
|
||||
});
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={!isCompletedStream}>
|
||||
|
@ -71,7 +63,9 @@ export const ReasoningMessagePills: React.FC<{
|
|||
animate={pills.length > 0 ? 'visible' : 'hidden'}
|
||||
className={'flex w-full flex-wrap gap-1.5 overflow-hidden'}>
|
||||
{pills.map((pill) => (
|
||||
<Pill key={pill.id} useAnimation={useAnimation} {...pill} onClick={onClick} />
|
||||
<Link href={makeHref(pill)}>
|
||||
<Pill key={pill.id} useAnimation={useAnimation} {...pill} />
|
||||
</Link>
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
|
|
@ -78,6 +78,8 @@ const InteractiveLoadingWrapper = () => {
|
|||
setPillContainers([...pillContainers, newContainer]);
|
||||
};
|
||||
|
||||
const chatId = '123';
|
||||
|
||||
const message: BusterChatMessageReasoning_pills = {
|
||||
...mockReasoningMessage,
|
||||
status: 'loading',
|
||||
|
@ -89,7 +91,7 @@ const InteractiveLoadingWrapper = () => {
|
|||
<Button onClick={addNewContainer} variant="default">
|
||||
Add New Container
|
||||
</Button>
|
||||
<ReasoningMessagePillsContainer {...message} isCompletedStream={false} />
|
||||
<ReasoningMessagePillsContainer {...message} chatId={chatId} isCompletedStream={false} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -32,8 +32,9 @@ export const ReasoningMessagePillsContainer: React.FC<
|
|||
BusterChatMessageReasoning_pills & {
|
||||
status: NonNullable<BusterChatMessageReasoning_pills['status']>;
|
||||
isCompletedStream: boolean;
|
||||
chatId: string;
|
||||
}
|
||||
> = ({ pill_containers, status, isCompletedStream }) => {
|
||||
> = ({ pill_containers, status, isCompletedStream, chatId }) => {
|
||||
const hasPills = !!pill_containers && pill_containers.length > 0;
|
||||
|
||||
if (!hasPills) return null;
|
||||
|
@ -50,6 +51,7 @@ export const ReasoningMessagePillsContainer: React.FC<
|
|||
key={index}
|
||||
pillContainer={pill_container}
|
||||
isCompletedStream={isCompletedStream}
|
||||
chatId={chatId}
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useGetChatMessage } from '@/api/buster_rest/chats';
|
|||
import { ReasoningMessagePillsContainer } from './ReasoningMessagePillsContainer';
|
||||
|
||||
export const ReasoningMessage_PillsContainer: React.FC<ReasoningMessageProps> = React.memo(
|
||||
({ reasoningMessageId, messageId, isCompletedStream }) => {
|
||||
({ reasoningMessageId, messageId, isCompletedStream, chatId }) => {
|
||||
const reasoningMessage = useGetChatMessage(
|
||||
messageId,
|
||||
(x) => x?.reasoning_messages[reasoningMessageId]
|
||||
|
@ -19,6 +19,7 @@ export const ReasoningMessage_PillsContainer: React.FC<ReasoningMessageProps> =
|
|||
{...reasoningMessagePills}
|
||||
status={status}
|
||||
isCompletedStream={isCompletedStream}
|
||||
chatId={chatId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
'use server';
|
||||
|
||||
import React from 'react';
|
||||
import { ShareAssetType } from '@/api/asset_interfaces';
|
||||
import { AppPasswordAccess } from '@/controllers/AppPasswordAccess';
|
||||
import { AppNoPageAccess } from '@/controllers/AppNoPageAccess';
|
||||
import { prefetchAssetCheck } from '@/api/buster_rest/assets';
|
||||
import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
|
||||
export type AppAssetCheckLayoutProps = {
|
||||
assetId: string;
|
||||
|
|
|
@ -55,13 +55,6 @@ export const ChatResponseMessage_File: React.FC<ChatResponseMessageProps> = Reac
|
|||
return '';
|
||||
}, [chatId, file_type, id]);
|
||||
|
||||
const onClick = useMemoizedFn(() => {
|
||||
onSetSelectedFile({
|
||||
id,
|
||||
type: file_type
|
||||
});
|
||||
});
|
||||
|
||||
useMount(() => {
|
||||
if (href) {
|
||||
router.prefetch(href);
|
||||
|
@ -69,11 +62,10 @@ export const ChatResponseMessage_File: React.FC<ChatResponseMessageProps> = Reac
|
|||
});
|
||||
|
||||
return (
|
||||
<Link href={href} prefetch>
|
||||
<Link href={href} prefetch onClick={() => onSetSelectedFile({ id, type: file_type })}>
|
||||
<StreamingMessage_File
|
||||
isCompletedStream={isCompletedStream}
|
||||
responseMessage={responseMessage}
|
||||
onClick={onClick}
|
||||
isSelectedFile={isSelectedFile}
|
||||
/>
|
||||
</Link>
|
||||
|
|
|
@ -27,6 +27,7 @@ export const ChatResponseMessages: React.FC<ChatResponseMessagesProps> = React.m
|
|||
finalReasoningMessage={finalReasoningMessage}
|
||||
isCompletedStream={isCompletedStream}
|
||||
messageId={messageId}
|
||||
chatId={chatId}
|
||||
/>
|
||||
|
||||
{responseMessageIds.map((responseMessageId, index) => (
|
||||
|
|
|
@ -10,6 +10,8 @@ import { useChatLayoutContextSelector } from '../../../ChatLayoutContext';
|
|||
import { useGetChatMessage } from '@/api/buster_rest/chats';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
import Link from 'next/link';
|
||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||
|
||||
const animations = {
|
||||
initial: { opacity: 0 },
|
||||
|
@ -23,7 +25,8 @@ export const ChatResponseReasoning: React.FC<{
|
|||
finalReasoningMessage: string | undefined | null;
|
||||
isCompletedStream: boolean;
|
||||
messageId: string;
|
||||
}> = React.memo(({ reasoningMessageId, isCompletedStream, messageId }) => {
|
||||
chatId: string;
|
||||
}> = React.memo(({ reasoningMessageId, isCompletedStream, messageId, chatId }) => {
|
||||
const lastMessageTitle = useGetChatMessage(
|
||||
messageId,
|
||||
(x) => x?.reasoning_messages?.[reasoningMessageId ?? '']?.title
|
||||
|
@ -46,29 +49,31 @@ export const ChatResponseReasoning: React.FC<{
|
|||
return lastMessageTitle || 'Thinking...';
|
||||
}, [lastMessageTitle, finalReasoningMessage, blackBoxMessage]);
|
||||
|
||||
const onClickReasoning = useMemoizedFn(() => {
|
||||
onSetSelectedFile({
|
||||
type: 'reasoning',
|
||||
id: messageId
|
||||
const href = useMemo(() => {
|
||||
return createBusterRoute({
|
||||
route: BusterRoutes.APP_CHAT_ID_REASONING_ID,
|
||||
messageId,
|
||||
chatId
|
||||
});
|
||||
});
|
||||
}, [isReasonginFileSelected, messageId]);
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={!isCompletedStream} mode="wait">
|
||||
<motion.div
|
||||
{...animations}
|
||||
key={text}
|
||||
className="mb-3.5 flex h-[14px] max-h-[14px] w-fit cursor-pointer items-center"
|
||||
onClick={onClickReasoning}>
|
||||
{!showShimmerText ? (
|
||||
<Text variant={'secondary'} className="hover:text-text-default hover:underline">
|
||||
{text}
|
||||
</Text>
|
||||
) : (
|
||||
<ShimmerText text={text ?? ''} />
|
||||
)}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
<Link href={href}>
|
||||
<AnimatePresence initial={!isCompletedStream} mode="wait">
|
||||
<motion.div
|
||||
{...animations}
|
||||
key={text}
|
||||
className="mb-3.5 flex h-[14px] max-h-[14px] w-fit cursor-pointer items-center">
|
||||
{!showShimmerText ? (
|
||||
<Text variant={'secondary'} className="hover:text-text-default hover:underline">
|
||||
{text}
|
||||
</Text>
|
||||
) : (
|
||||
<ShimmerText text={text ?? ''} />
|
||||
)}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export const ChatContainerHeaderDropdown: React.FC<{
|
|||
chatId &&
|
||||
deleteChat([chatId], {
|
||||
onSuccess: () => {
|
||||
onChangePage(createBusterRoute({ route: BusterRoutes.APP_CHAT }));
|
||||
onChangePage({ route: BusterRoutes.APP_CHAT });
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -50,7 +50,8 @@ const useChatIndividualContext = ({
|
|||
useAutoChangeLayout({
|
||||
lastMessageId: currentMessageId,
|
||||
onSetSelectedFile,
|
||||
selectedFileId
|
||||
selectedFileId,
|
||||
chatId
|
||||
});
|
||||
|
||||
return React.useMemo(
|
||||
|
|
|
@ -5,15 +5,19 @@ import type { SelectedFile } from '../interfaces';
|
|||
import { useEffect, useRef } from 'react';
|
||||
import findLast from 'lodash/findLast';
|
||||
import { BusterChatResponseMessage_file } from '@/api/asset_interfaces/chat';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { BusterRoutes } from '@/routes';
|
||||
|
||||
export const useAutoChangeLayout = ({
|
||||
lastMessageId,
|
||||
onSetSelectedFile,
|
||||
selectedFileId
|
||||
selectedFileId,
|
||||
chatId
|
||||
}: {
|
||||
lastMessageId: string;
|
||||
onSetSelectedFile: (file: SelectedFile) => void;
|
||||
selectedFileId: string | undefined;
|
||||
chatId: string | undefined;
|
||||
}) => {
|
||||
const previousLastMessageId = useRef<string | null>(null);
|
||||
const reasoningMessagesLength = useGetChatMessage(
|
||||
|
@ -27,9 +31,12 @@ export const useAutoChangeLayout = ({
|
|||
|
||||
//change the page to reasoning file if we get a reasoning message
|
||||
useEffect(() => {
|
||||
// console.log(isCompletedStream, hasReasoning, lastMessageId, previousLastMessageId.current);
|
||||
if (!isCompletedStream && hasReasoning && previousLastMessageId.current !== lastMessageId) {
|
||||
// hasSeeningReasoningPage.current = true;
|
||||
if (
|
||||
!isCompletedStream &&
|
||||
hasReasoning &&
|
||||
previousLastMessageId.current !== lastMessageId &&
|
||||
chatId
|
||||
) {
|
||||
onSetSelectedFile({ id: lastMessageId, type: 'reasoning' });
|
||||
previousLastMessageId.current = lastMessageId;
|
||||
}
|
||||
|
@ -48,5 +55,5 @@ export const useAutoChangeLayout = ({
|
|||
onSetSelectedFile({ id: lastFileId, type: lastFile.file_type });
|
||||
}
|
||||
}
|
||||
}, [isCompletedStream, hasReasoning, lastMessageId]);
|
||||
}, [isCompletedStream, chatId, hasReasoning, lastMessageId]);
|
||||
};
|
||||
|
|
|
@ -57,6 +57,6 @@ export const createChatAssetRoute = ({
|
|||
type: FileType;
|
||||
}) => {
|
||||
const routeBuilder = chatRouteRecord[type];
|
||||
if (!routeBuilder) return '';
|
||||
if (!routeBuilder) return null;
|
||||
return routeBuilder(chatId, assetId);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,9 @@ import { useMemoizedFn } from '@/hooks';
|
|||
import { createSelectedFile } from './createSelectedFile';
|
||||
import type { useGetChatParams } from '../useGetChatParams';
|
||||
import type { AppSplitterRef } from '@/components/ui/layouts/AppSplitter';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||
import { createChatAssetRoute } from '../helpers';
|
||||
|
||||
export const useSelectedFile = ({
|
||||
animateOpenSplitter,
|
||||
|
@ -17,6 +20,7 @@ export const useSelectedFile = ({
|
|||
chatParams: ReturnType<typeof useGetChatParams>;
|
||||
}) => {
|
||||
const { metricVersionNumber, dashboardVersionNumber } = chatParams;
|
||||
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
|
||||
|
||||
const selectedFile: SelectedFile | null = useMemo(() => {
|
||||
return createSelectedFile(chatParams);
|
||||
|
@ -29,13 +33,24 @@ export const useSelectedFile = ({
|
|||
}, [selectedFile?.type, metricVersionNumber, dashboardVersionNumber]);
|
||||
|
||||
/**
|
||||
* @description Opens the splitter if the file is not already open. If the file is already open, it will collapse the splitter. This does NOT set the selected file. You should do that with a Link
|
||||
* @description Opens the splitter if the file is not already open.
|
||||
* If the file is already open, it will collapse the splitter.
|
||||
* You should do try to set the selected file with a Link!
|
||||
* @param file
|
||||
*/
|
||||
const onSetSelectedFile = useMemoizedFn(async (file: SelectedFile | null) => {
|
||||
const handleFileCollapse =
|
||||
!file || (file?.id === selectedFile?.id && !appSplitterRef.current?.isSideClosed('right'));
|
||||
|
||||
if (file && chatParams.chatId) {
|
||||
const link = createChatAssetRoute({
|
||||
chatId: chatParams.chatId,
|
||||
assetId: file?.id,
|
||||
type: file?.type
|
||||
});
|
||||
if (link) onChangePage(link);
|
||||
}
|
||||
|
||||
if (handleFileCollapse) {
|
||||
animateOpenSplitter('left');
|
||||
return;
|
||||
|
|
|
@ -90,7 +90,7 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
|||
),
|
||||
[]
|
||||
)}
|
||||
headerClassName="border-l">
|
||||
headerClassName="">
|
||||
<AppSplitter
|
||||
ref={appSplitterRef}
|
||||
autoSaveId={autoSaveId}
|
||||
|
|
|
@ -193,7 +193,7 @@ const useDeleteDashboardSelectMenu = ({ dashboardId }: { dashboardId: string })
|
|||
icon: <Trash />,
|
||||
onClick: async () => {
|
||||
await deleteDashboard({ dashboardId });
|
||||
onChangePage(createBusterRoute({ route: BusterRoutes.APP_DASHBOARDS }));
|
||||
onChangePage({ route: BusterRoutes.APP_DASHBOARDS });
|
||||
}
|
||||
}),
|
||||
[dashboardId]
|
||||
|
|
Loading…
Reference in New Issue