mirror of https://github.com/buster-so/buster.git
version id update
This commit is contained in:
parent
29b2c8996f
commit
330c3067d5
|
@ -39,19 +39,20 @@ export type BusterChatMessage_thought = {
|
||||||
thought_secondary_title: string;
|
thought_secondary_title: string;
|
||||||
thought_pills?: BusterChatMessage_thoughtPill[];
|
thought_pills?: BusterChatMessage_thoughtPill[];
|
||||||
hidden?: boolean; //if left undefined, will automatically be set to false if stream has ended
|
hidden?: boolean; //if left undefined, will automatically be set to false if stream has ended
|
||||||
in_progress?: boolean; //if left undefined, will automatically be set to true if the chat stream is in progress AND there is no message after it
|
status?: 'loading' | 'completed' | 'failed'; //if left undefined, will automatically be set to 'loading' if the chat stream is in progress AND there is no message after it
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BusterChatMessage_fileMetadata = {
|
export type BusterChatMessage_fileMetadata = {
|
||||||
status: 'loading' | 'completed' | 'failed';
|
status: 'loading' | 'completed' | 'failed';
|
||||||
message: string;
|
message: string;
|
||||||
timestamp?: number;
|
timestamp?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BusterChatMessage_file = {
|
export type BusterChatMessage_file = {
|
||||||
id: string;
|
id: string;
|
||||||
type: 'file';
|
type: 'file';
|
||||||
file_type: FileType;
|
file_type: FileType;
|
||||||
|
file_name: string;
|
||||||
version_number: number;
|
version_number: number;
|
||||||
version_id: string;
|
version_id: string;
|
||||||
metadata?: BusterChatMessage_fileMetadata[];
|
metadata?: BusterChatMessage_fileMetadata[];
|
||||||
|
|
|
@ -1,24 +1,64 @@
|
||||||
import React from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Input, Button } from 'antd';
|
import { Input, Button } from 'antd';
|
||||||
import { Text } from '@/components/text';
|
import { Text } from '@/components/text';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { AppMaterialIcons } from '@/components';
|
||||||
|
import { inputHasText } from '@/utils';
|
||||||
|
|
||||||
export const ChatInput: React.FC = () => {
|
const autoSize = { minRows: 4, maxRows: 4 };
|
||||||
|
|
||||||
|
export const ChatInput: React.FC = React.memo(() => {
|
||||||
const { styles, cx } = useStyles();
|
const { styles, cx } = useStyles();
|
||||||
|
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
|
||||||
|
const disableSendButton = useMemo(() => {
|
||||||
|
return !inputHasText(inputValue);
|
||||||
|
}, [inputValue]);
|
||||||
|
|
||||||
|
const handleInputChange = useMemoizedFn((e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = useMemoizedFn(async () => {
|
||||||
|
if (disableSendButton) return;
|
||||||
|
console.log('submit');
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
styles.inputContainer,
|
styles.inputContainer,
|
||||||
'relative z-10 flex flex-col items-center space-y-1 px-3 pb-2 pt-0.5'
|
'relative z-10 flex flex-col items-center space-y-1.5 px-3 pb-2 pt-0.5'
|
||||||
)}>
|
)}>
|
||||||
<Input.TextArea />
|
<div className="relative w-full">
|
||||||
|
<Input.TextArea
|
||||||
|
className="w-full"
|
||||||
|
defaultValue={inputValue}
|
||||||
|
placeholder="Edit or follow up"
|
||||||
|
rows={4}
|
||||||
|
autoSize={autoSize}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="absolute bottom-2 right-2">
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
disabled={disableSendButton}
|
||||||
|
icon={<AppMaterialIcons icon="arrow_upward" />}
|
||||||
|
className=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Text size="xs" type="tertiary">
|
<Text size="xs" type="tertiary">
|
||||||
Our AI may make mistakes. Check important info.
|
Our AI may make mistakes. Check important info.
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
|
ChatInput.displayName = 'ChatInput';
|
||||||
|
|
||||||
const useStyles = createStyles(({ token, css }) => ({
|
const useStyles = createStyles(({ token, css }) => ({
|
||||||
inputContainer: css`
|
inputContainer: css`
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { ChatResponseMessageProps } from './ChatResponseMessages';
|
|
||||||
|
|
||||||
export const ChatResponseMessage_File: React.FC<ChatResponseMessageProps> = React.memo(
|
|
||||||
({ responseMessage }) => {
|
|
||||||
return <div>ChatResponseMessage_File</div>;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ChatResponseMessage_File.displayName = 'ChatResponseMessage_File';
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { ChatResponseMessageProps } from '../ChatResponseMessages';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import type {
|
||||||
|
BusterChatMessage_file,
|
||||||
|
BusterChatMessage_fileMetadata
|
||||||
|
} from '@/api/buster_socket/chats';
|
||||||
|
import { Text } from '@/components/text';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { animationConfig } from '../animationConfig';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { StatusIndicator } from '../StatusIndicator';
|
||||||
|
import { formatDate } from '@/utils';
|
||||||
|
|
||||||
|
export const ChatResponseMessage_File: React.FC<ChatResponseMessageProps> = React.memo(
|
||||||
|
({ responseMessage: responseMessageProp, isCompletedStream }) => {
|
||||||
|
const { cx, styles } = useStyles();
|
||||||
|
const responseMessage = responseMessageProp as BusterChatMessage_file;
|
||||||
|
const { file_name, file_type, version_id, version_number, id, metadata = [] } = responseMessage;
|
||||||
|
|
||||||
|
const onClickCard = useMemoizedFn(() => {
|
||||||
|
console.log('clicked');
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatePresence initial={!isCompletedStream}>
|
||||||
|
<motion.div
|
||||||
|
id={id}
|
||||||
|
{...animationConfig}
|
||||||
|
className={cx(styles.fileCard, 'file-card flex flex-col overflow-hidden')}>
|
||||||
|
<VerticalDivider className="top-line mb-0.5" />
|
||||||
|
<div
|
||||||
|
onClick={onClickCard}
|
||||||
|
className={cx(
|
||||||
|
styles.fileContainer,
|
||||||
|
'transition-shadow duration-300',
|
||||||
|
'flex flex-col items-center',
|
||||||
|
'cursor-pointer overflow-hidden'
|
||||||
|
)}>
|
||||||
|
<ChatResponseMessageHeader file_name={file_name} version_number={version_number} />
|
||||||
|
<ChatResponseMessageBody metadata={metadata} />
|
||||||
|
</div>
|
||||||
|
<VerticalDivider className="bottom-line mt-0.5" />
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ChatResponseMessage_File.displayName = 'ChatResponseMessage_File';
|
||||||
|
|
||||||
|
const ChatResponseMessageHeader: React.FC<{ file_name: string; version_number: number }> = ({
|
||||||
|
file_name,
|
||||||
|
version_number
|
||||||
|
}) => {
|
||||||
|
const { cx, styles } = useStyles();
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
styles.fileHeader,
|
||||||
|
'file-header',
|
||||||
|
'flex w-full items-center space-x-1.5 overflow-hidden px-2.5'
|
||||||
|
)}>
|
||||||
|
<Text className="truncate">{file_name}</Text>
|
||||||
|
<div className={cx(styles.fileVersion, 'flex items-center space-x-1.5')}>
|
||||||
|
<Text type="secondary" lineHeight={11} size="sm">
|
||||||
|
v{version_number}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChatResponseMessageBody: React.FC<{
|
||||||
|
metadata: BusterChatMessage_fileMetadata[];
|
||||||
|
}> = ({ metadata }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col items-center space-y-0.5 px-2.5 py-2">
|
||||||
|
{metadata.map((metadata, index) => (
|
||||||
|
<MetadataItem metadata={metadata} key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MetadataItem: React.FC<{ metadata: BusterChatMessage_fileMetadata }> = ({ metadata }) => {
|
||||||
|
const { status, message, timestamp } = metadata;
|
||||||
|
|
||||||
|
const timestampFormatted = useMemo(() => {
|
||||||
|
if (!timestamp) return '';
|
||||||
|
return formatDate({
|
||||||
|
date: timestamp,
|
||||||
|
format: 'll'
|
||||||
|
});
|
||||||
|
}, [timestamp]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full items-center justify-start space-x-1.5 overflow-hidden">
|
||||||
|
<div>
|
||||||
|
<StatusIndicator status={status} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Text className="truncate" size="xs" type="secondary">
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{timestamp && (
|
||||||
|
<Text type="tertiary" className="truncate" size="xs">
|
||||||
|
{timestampFormatted}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const VerticalDivider: React.FC<{ className?: string }> = ({ className }) => {
|
||||||
|
const { cx, styles } = useStyles();
|
||||||
|
return <div className={cx(styles.verticalDivider, 'vertical-divider', className)} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ token, css }) => ({
|
||||||
|
fileCard: css`
|
||||||
|
& + .file-card {
|
||||||
|
.vertical-divider.top-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.file-card {
|
||||||
|
.thought-card + & {
|
||||||
|
.vertical-divider.top-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
.vertical-divider.bottom-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fileContainer: css`
|
||||||
|
border-radius: ${token.borderRadius}px;
|
||||||
|
border: 0.5px solid ${token.colorBorder};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: ${token.colorTextTertiary};
|
||||||
|
box-shadow: ${token.boxShadowTertiary};
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: black;
|
||||||
|
box-shadow: ${token.boxShadowTertiary};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fileHeader: css`
|
||||||
|
background: ${token.controlItemBgActive};
|
||||||
|
border-bottom: 0.5px solid ${token.colorBorder};
|
||||||
|
height: 32px;
|
||||||
|
`,
|
||||||
|
fileVersion: css`
|
||||||
|
border-radius: ${token.borderRadius}px;
|
||||||
|
padding: 4px;
|
||||||
|
background: ${token.colorFillTertiary};
|
||||||
|
`,
|
||||||
|
verticalDivider: css`
|
||||||
|
height: 9px;
|
||||||
|
width: 0.5px;
|
||||||
|
margin-left: 8px;
|
||||||
|
background: ${token.colorTextTertiary};
|
||||||
|
`
|
||||||
|
}));
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './ChatResponseMessage_File';
|
|
@ -30,7 +30,7 @@ export const ChatResponseMessage_Text: React.FC<ChatResponseMessageProps> = Reac
|
||||||
}, [responseMessage?.message_chunk, responseMessage?.message]);
|
}, [responseMessage?.message_chunk, responseMessage?.message]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="text-card">
|
||||||
{textChunks.map((chunk, index) => (
|
{textChunks.map((chunk, index) => (
|
||||||
<AnimatePresence key={index} initial={!isCompletedStream}>
|
<AnimatePresence key={index} initial={!isCompletedStream}>
|
||||||
<motion.span {...animationConfig}>{chunk}</motion.span>
|
<motion.span {...animationConfig}>{chunk}</motion.span>
|
||||||
|
|
|
@ -6,24 +6,28 @@ import { animationConfig } from '../animationConfig';
|
||||||
import { Text } from '@/components/text';
|
import { Text } from '@/components/text';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import { PillContainer } from './ChatResponseMessage_ThoughtPills';
|
import { PillContainer } from './ChatResponseMessage_ThoughtPills';
|
||||||
import { StatusIndicator } from './StatusIndicator';
|
import { StatusIndicator } from '../StatusIndicator';
|
||||||
import { VerticalBar } from './VerticalBar';
|
import { VerticalBar } from './VerticalBar';
|
||||||
|
|
||||||
export const ChatResponseMessage_Thought: React.FC<ChatResponseMessageProps> = React.memo(
|
export const ChatResponseMessage_Thought: React.FC<ChatResponseMessageProps> = React.memo(
|
||||||
({ responseMessage: responseMessageProp, isCompletedStream, isLastMessageItem }) => {
|
({ responseMessage: responseMessageProp, isCompletedStream, isLastMessageItem }) => {
|
||||||
const responseMessage = responseMessageProp as BusterChatMessage_thought;
|
const responseMessage = responseMessageProp as BusterChatMessage_thought;
|
||||||
const { thought_title, thought_secondary_title, thought_pills, in_progress } = responseMessage;
|
const { thought_title, thought_secondary_title, thought_pills, status } = responseMessage;
|
||||||
const { cx } = useStyles();
|
const { cx } = useStyles();
|
||||||
const hasPills = thought_pills && thought_pills.length > 0;
|
const hasPills = thought_pills && thought_pills.length > 0;
|
||||||
|
|
||||||
const showLoadingIndicator = in_progress ?? (isLastMessageItem && !isCompletedStream);
|
const showLoadingIndicator =
|
||||||
|
(status ?? (isLastMessageItem && !isCompletedStream)) ? 'loading' : 'completed';
|
||||||
|
const inProgress = showLoadingIndicator === 'loading';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence initial={!isCompletedStream}>
|
<AnimatePresence initial={!isCompletedStream}>
|
||||||
<motion.div className={cx('relative flex space-x-1.5')} {...animationConfig}>
|
<motion.div
|
||||||
|
className={cx('relative flex space-x-1.5', 'thought-card')}
|
||||||
|
{...animationConfig}>
|
||||||
<div className="flex w-4 min-w-4 flex-col items-center pt-0.5">
|
<div className="flex w-4 min-w-4 flex-col items-center pt-0.5">
|
||||||
<StatusIndicator inProgress={showLoadingIndicator} />
|
<StatusIndicator status={showLoadingIndicator} />
|
||||||
<VerticalBar inProgress={in_progress} hasPills={hasPills} />
|
<VerticalBar inProgress={inProgress} hasPills={hasPills} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col space-y-2">
|
<div className="flex w-full flex-col space-y-2">
|
||||||
<div className="flex w-full items-center space-x-1.5 overflow-hidden">
|
<div className="flex w-full items-center space-x-1.5 overflow-hidden">
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import {
|
import type {
|
||||||
BusterChatMessage_thought,
|
BusterChatMessage_thought,
|
||||||
BusterChatMessage_thoughtPill
|
BusterChatMessage_thoughtPill
|
||||||
} from '@/api/buster_socket/chats';
|
} from '@/api/buster_socket/chats';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { calculateTextWidth } from '@/utils';
|
import { calculateTextWidth } from '@/utils';
|
||||||
import { useDebounce, useMemoizedFn, useSize } from 'ahooks';
|
import { useDebounce, useMemoizedFn, useSize } from 'ahooks';
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
|
||||||
import { createStyles } from 'antd-style';
|
|
||||||
import { CircleSpinnerLoader } from '@/components/loaders/CircleSpinnerLoader';
|
|
||||||
import { AppMaterialIcons } from '@/components/icons';
|
|
||||||
|
|
||||||
const animationConfig = {
|
|
||||||
initial: { opacity: 0 },
|
|
||||||
animate: { opacity: 1 },
|
|
||||||
exit: { opacity: 0 },
|
|
||||||
transition: { duration: 0.25 }
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StatusIndicator: React.FC<{ inProgress?: boolean }> = React.memo(({ inProgress }) => {
|
|
||||||
const { styles, cx } = useStyles();
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cx(
|
|
||||||
styles.indicatorContainer,
|
|
||||||
inProgress && 'in-progress',
|
|
||||||
'flex items-center justify-center transition-all delay-100 duration-300'
|
|
||||||
)}>
|
|
||||||
<AnimatePresence mode="wait">
|
|
||||||
<motion.div
|
|
||||||
key={inProgress ? 'in-progress' : 'completed'}
|
|
||||||
{...animationConfig}
|
|
||||||
className={cx(
|
|
||||||
styles.indicator,
|
|
||||||
inProgress && 'in-progress',
|
|
||||||
'flex items-center justify-center transition-all duration-300'
|
|
||||||
)}>
|
|
||||||
{inProgress ? (
|
|
||||||
<CircleSpinnerLoader size={8} />
|
|
||||||
) : (
|
|
||||||
<AppMaterialIcons className="" icon="check" size={6} />
|
|
||||||
)}
|
|
||||||
</motion.div>
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
StatusIndicator.displayName = 'StatusIndicator';
|
|
||||||
|
|
||||||
const useStyles = createStyles(({ token, css }) => ({
|
|
||||||
indicatorContainer: css`
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background-color: ${token.colorTextPlaceholder};
|
|
||||||
border-radius: 100%;
|
|
||||||
|
|
||||||
&.in-progress {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
indicator: css`
|
|
||||||
color: white;
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: 100%;
|
|
||||||
background-color: ${token.colorTextPlaceholder};
|
|
||||||
box-shadow: 0 0 0 0.7px white inset;
|
|
||||||
|
|
||||||
&.in-progress {
|
|
||||||
background-color: transparent;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}));
|
|
|
@ -30,7 +30,7 @@ export const ChatResponseMessages: React.FC<ChatResponseMessagesProps> = React.m
|
||||||
const lastMessageIndex = responseMessages.length - 1;
|
const lastMessageIndex = responseMessages.length - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageContainer className="flex w-full flex-col space-y-1">
|
<MessageContainer className="flex w-full flex-col space-y-1 overflow-hidden">
|
||||||
{responseMessages.map((responseMessage, index) => {
|
{responseMessages.map((responseMessage, index) => {
|
||||||
const ChatResponseMessage = ChatResponseMessageRecord[responseMessage.type];
|
const ChatResponseMessage = ChatResponseMessageRecord[responseMessage.type];
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import { CircleSpinnerLoader } from '@/components/loaders/CircleSpinnerLoader';
|
||||||
|
import { AppMaterialIcons } from '@/components/icons';
|
||||||
|
|
||||||
|
const animationConfig = {
|
||||||
|
initial: { opacity: 0 },
|
||||||
|
animate: { opacity: 1 },
|
||||||
|
exit: { opacity: 0 },
|
||||||
|
transition: { duration: 0.25 }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StatusIndicator: React.FC<{ status?: 'completed' | 'loading' | 'failed' }> =
|
||||||
|
React.memo(({ status }) => {
|
||||||
|
const { styles, cx } = useStyles();
|
||||||
|
const inProgress = status === 'loading';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
styles.indicatorContainer,
|
||||||
|
inProgress && 'in-progress',
|
||||||
|
'flex items-center justify-center transition-all delay-100 duration-300'
|
||||||
|
)}>
|
||||||
|
<AnimatePresence mode="wait">
|
||||||
|
<motion.div
|
||||||
|
key={inProgress ? 'in-progress' : 'completed'}
|
||||||
|
{...animationConfig}
|
||||||
|
className={cx(
|
||||||
|
styles.indicator,
|
||||||
|
inProgress && 'in-progress',
|
||||||
|
'flex items-center justify-center transition-all duration-300'
|
||||||
|
)}>
|
||||||
|
{inProgress ? (
|
||||||
|
<CircleSpinnerLoader size={8} />
|
||||||
|
) : (
|
||||||
|
<AppMaterialIcons className="" icon="check" size={6} />
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
StatusIndicator.displayName = 'StatusIndicator';
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ token, css }) => ({
|
||||||
|
indicatorContainer: css`
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: ${token.colorTextPlaceholder};
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
&.in-progress {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
indicator: css`
|
||||||
|
color: white;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: ${token.colorTextPlaceholder};
|
||||||
|
box-shadow: 0 0 0 0.7px white inset;
|
||||||
|
|
||||||
|
&.in-progress {
|
||||||
|
background-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}));
|
|
@ -11,7 +11,7 @@ export const MessageContainer: React.FC<{
|
||||||
}> = React.memo(({ children, senderName, senderId, senderAvatar, className = '' }) => {
|
}> = React.memo(({ children, senderName, senderId, senderAvatar, className = '' }) => {
|
||||||
const { cx } = useStyles();
|
const { cx } = useStyles();
|
||||||
return (
|
return (
|
||||||
<div className={cx('flex w-full space-x-2')}>
|
<div className={cx('flex w-full space-x-2 overflow-hidden')}>
|
||||||
{senderName ? (
|
{senderName ? (
|
||||||
<BusterUserAvatar size={24} name={senderName} src={senderAvatar} useToolTip={true} />
|
<BusterUserAvatar size={24} name={senderName} src={senderAvatar} useToolTip={true} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {
|
||||||
type BusterChatMessageRequest,
|
type BusterChatMessageRequest,
|
||||||
type BusterChatMessageResponse,
|
type BusterChatMessageResponse,
|
||||||
FileType,
|
FileType,
|
||||||
BusterChatMessage_thoughtPill
|
BusterChatMessage_thoughtPill,
|
||||||
|
BusterChatMessage_fileMetadata
|
||||||
} from '@/api/buster_socket/chats';
|
} from '@/api/buster_socket/chats';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
@ -45,17 +46,31 @@ export const createMockResponseMessageThought = (): BusterChatMessage_thought =>
|
||||||
thought_secondary_title: faker.lorem.word(),
|
thought_secondary_title: faker.lorem.word(),
|
||||||
thought_pills: fourRandomPills,
|
thought_pills: fourRandomPills,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
in_progress: undefined
|
status: undefined
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const createMockResponseMessageFile = (): BusterChatMessage_file => {
|
const createMockResponseMessageFile = (): BusterChatMessage_file => {
|
||||||
|
const randomMetadataCount = faker.number.int(3);
|
||||||
|
const randomMetadata: BusterChatMessage_fileMetadata[] = Array.from(
|
||||||
|
{ length: randomMetadataCount },
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
status: 'completed',
|
||||||
|
message: faker.lorem.sentence(),
|
||||||
|
timestamp: faker.date.recent().toISOString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: faker.string.uuid(),
|
id: faker.string.uuid(),
|
||||||
type: 'file',
|
type: 'file',
|
||||||
file_type: 'metric',
|
file_type: 'metric',
|
||||||
version_number: 1,
|
version_number: 1,
|
||||||
version_id: faker.string.uuid()
|
version_id: faker.string.uuid(),
|
||||||
|
file_name: faker.company.name(),
|
||||||
|
metadata: randomMetadata
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,22 +87,8 @@ export const MOCK_CHAT: BusterChat = {
|
||||||
createMockResponseMessageText(),
|
createMockResponseMessageText(),
|
||||||
createMockResponseMessageThought(),
|
createMockResponseMessageThought(),
|
||||||
createMockResponseMessageThought(),
|
createMockResponseMessageThought(),
|
||||||
createMockResponseMessageThought(),
|
createMockResponseMessageFile(),
|
||||||
createMockResponseMessageThought(),
|
createMockResponseMessageFile()
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought(),
|
|
||||||
createMockResponseMessageThought()
|
|
||||||
// createMockResponseMessageFile(),
|
|
||||||
// createMockResponseMessageFile()
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue